audiences 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/audiences/contexts_controller.rb +38 -2
- data/app/controllers/audiences/scim_proxy_controller.rb +5 -10
- data/app/models/audiences/context.rb +24 -4
- data/app/models/audiences/context_users.rb +32 -0
- data/app/models/audiences/criterion.rb +20 -0
- data/app/models/audiences/criterion_users.rb +27 -0
- data/app/models/audiences/external_user.rb +21 -0
- data/app/models/audiences/membership.rb +8 -0
- data/app/models/audiences/users_search.rb +33 -0
- data/app/models/concerns/audiences/membership_group.rb +14 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20230725194934_create_audiences_contexts.rb +2 -1
- data/db/migrate/20231114210454_add_extra_users_to_audiences_context.rb +7 -0
- data/db/migrate/20231114210633_remove_criteria_from_audiences_context.rb +7 -0
- data/db/migrate/20231114215843_create_audiences_criterions.rb +12 -0
- data/db/migrate/20231115204932_add_users_to_audiences_criterion.rb +8 -0
- data/db/migrate/20231130165945_create_audiences_external_users.rb +13 -0
- data/db/migrate/20231130172718_create_audiences_memberships.rb +12 -0
- data/db/migrate/20231205140046_remove_serialized_users_from_criterion.rb +7 -0
- data/docs/CHANGELOG.md +3 -0
- data/docs/README.md +29 -14
- data/lib/audiences/notifications.rb +36 -0
- data/lib/audiences/scim/client.rb +29 -0
- data/lib/audiences/scim/resources_query.rb +56 -0
- data/lib/audiences/scim.rb +19 -0
- data/lib/audiences/version.rb +1 -1
- data/lib/audiences.rb +29 -1
- data/lib/tasks/audiences_tasks.rake +1 -0
- metadata +25 -9
- data/app/views/layouts/audiences/application.html.erb +0 -15
- data/lib/audiences/scim_proxy.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47c34fc0460801d3066e1748062d3338ceafa2745fd810935a0f2d380d7945b6
|
4
|
+
data.tar.gz: 802064bc1f3bc4dde42557ebeef35146461828347b0f84f6f082e14d0bf7ef0f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50bceefe5a5d71c40137f69fa7044c9ced27e6bd58a0bc3a28d391e3a668b6d4987bd02983bb788daeb40239c93bd39dd19e87850ef2bcb4f94b6cc1a1950418
|
7
|
+
data.tar.gz: 90fd1d3931a8cf62addd66e0ccd6cd67720acf7ee3e6e4fa92aba9b6c1add78c5a079874b46b917089b6df4469951494ac4a3f32e3cabf181c2c2dc5882a88dc
|
@@ -3,13 +3,49 @@
|
|
3
3
|
module Audiences
|
4
4
|
class ContextsController < ApplicationController
|
5
5
|
def show
|
6
|
-
|
6
|
+
render_context Audiences.load(params.require(:key))
|
7
|
+
end
|
8
|
+
|
9
|
+
def update
|
10
|
+
render_context Audiences.update(params.require(:key), **context_params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def users
|
14
|
+
users = (current_criterion || current_context).users
|
15
|
+
search = UsersSearch.new(scope: users,
|
16
|
+
query: params[:search],
|
17
|
+
limit: params[:limit],
|
18
|
+
offset: params[:offset])
|
19
|
+
|
20
|
+
render json: search
|
7
21
|
end
|
8
22
|
|
9
23
|
private
|
10
24
|
|
11
25
|
def current_context
|
12
|
-
@current_context ||= Audiences.load(params
|
26
|
+
@current_context ||= Audiences.load(params.require(:key))
|
27
|
+
end
|
28
|
+
|
29
|
+
def current_criterion
|
30
|
+
return unless params[:criterion_id]
|
31
|
+
|
32
|
+
@current_criterion ||= current_context.criteria.find(params[:criterion_id])
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_context(context)
|
36
|
+
render json: context.as_json(
|
37
|
+
only: %i[match_all extra_users],
|
38
|
+
methods: %i[count],
|
39
|
+
include: { criteria: { only: %i[id groups], methods: %i[count] } }
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def context_params
|
44
|
+
params.permit(
|
45
|
+
:match_all,
|
46
|
+
criteria: [groups: {}],
|
47
|
+
extra_users: [:id, :displayName, { photos: %i[type value] }]
|
48
|
+
).to_h.symbolize_keys
|
13
49
|
end
|
14
50
|
end
|
15
51
|
end
|
@@ -1,19 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "audiences/scim_proxy"
|
4
|
-
|
5
3
|
module Audiences
|
6
4
|
class ScimProxyController < ApplicationController
|
7
5
|
def get
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
private
|
6
|
+
resources = Audiences::Scim.resources(
|
7
|
+
type: params[:scim_path].to_sym,
|
8
|
+
filter: params[:filter]
|
9
|
+
)
|
14
10
|
|
15
|
-
|
16
|
-
params.except(:scim_path, :controller, :action).permit!.to_h
|
11
|
+
render json: resources
|
17
12
|
end
|
18
13
|
end
|
19
14
|
end
|
@@ -1,15 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Audiences
|
4
|
+
# Represents a context where the group of users (audience) is relevant.
|
5
|
+
# It includes the current matching users and the criteria to match these
|
6
|
+
# users (#criteria, #match_all, #extra_users).
|
7
|
+
#
|
4
8
|
class Context < ApplicationRecord
|
9
|
+
include ::Audiences::MembershipGroup
|
10
|
+
|
5
11
|
belongs_to :owner, polymorphic: true
|
12
|
+
has_many :criteria, class_name: "Audiences::Criterion",
|
13
|
+
autosave: true,
|
14
|
+
dependent: :destroy
|
15
|
+
|
16
|
+
before_save if: :match_all do
|
17
|
+
self.criteria = []
|
18
|
+
self.extra_users = []
|
19
|
+
end
|
6
20
|
|
7
|
-
|
8
|
-
|
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!
|
9
27
|
end
|
10
28
|
|
11
|
-
def
|
12
|
-
|
29
|
+
def refresh_users!
|
30
|
+
criteria.each(&:refresh_users!)
|
31
|
+
update!(users: ContextUsers.new(self).to_a)
|
32
|
+
Notifications.publish(self)
|
13
33
|
end
|
14
34
|
end
|
15
35
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
# @private
|
5
|
+
class ContextUsers
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(context)
|
9
|
+
@context = context
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(...)
|
13
|
+
if @context.match_all
|
14
|
+
all_users.each(...)
|
15
|
+
else
|
16
|
+
matching_users.each(...)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def all_users
|
23
|
+
users = Scim.resources(type: :Users)
|
24
|
+
ExternalUser.wrap(users.all)
|
25
|
+
end
|
26
|
+
|
27
|
+
def matching_users
|
28
|
+
extras = ExternalUser.wrap(@context.extra_users)
|
29
|
+
[*extras, *@context.criteria.flat_map(&:users)].uniq
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
class Criterion < ApplicationRecord
|
5
|
+
include ::Audiences::MembershipGroup
|
6
|
+
|
7
|
+
belongs_to :context, class_name: "Audiences::Context"
|
8
|
+
|
9
|
+
def self.map(criteria)
|
10
|
+
Array(criteria).map { new(_1) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def refresh_users!
|
14
|
+
update!(
|
15
|
+
users: CriterionUsers.new(groups || {}).to_a,
|
16
|
+
refreshed_at: Time.current
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
# @private
|
5
|
+
class CriterionUsers
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
def initialize(groups)
|
9
|
+
@groups = groups
|
10
|
+
end
|
11
|
+
|
12
|
+
def each(...)
|
13
|
+
@groups.values
|
14
|
+
.map { |groups| groups_users(groups.pluck("id")) }
|
15
|
+
.reduce(&:&)
|
16
|
+
&.each(...)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def groups_users(group_ids)
|
22
|
+
filter = group_ids.map { "groups.value eq #{_1}" }.join(" OR ")
|
23
|
+
users = Audiences::Scim.resources(type: :Users, filter: filter)
|
24
|
+
ExternalUser.wrap(users.all)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
class ExternalUser < ApplicationRecord
|
5
|
+
has_many :memberships
|
6
|
+
|
7
|
+
def self.wrap(resources)
|
8
|
+
return [] unless resources&.any?
|
9
|
+
|
10
|
+
attrs = resources.map do |data|
|
11
|
+
{ user_id: data["id"], data: data, created_at: Time.current, updated_at: Time.current }
|
12
|
+
end
|
13
|
+
upsert_all(attrs, unique_by: :user_id) # rubocop:disable Rails/SkipsModelValidations
|
14
|
+
where(user_id: attrs.pluck(:user_id))
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_json(*)
|
18
|
+
data.as_json
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
class UsersSearch
|
5
|
+
DEFAULT_LIMIT = 20
|
6
|
+
|
7
|
+
def initialize(query:, limit: nil, offset: 0, scope: ExternalUser)
|
8
|
+
@scope = scope
|
9
|
+
@query = query
|
10
|
+
@limit = limit || DEFAULT_LIMIT
|
11
|
+
@offset = offset
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(*)
|
15
|
+
{
|
16
|
+
users: users,
|
17
|
+
count: count,
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
delegate :count, to: :result
|
22
|
+
|
23
|
+
def users
|
24
|
+
@users ||= result.limit(@limit).offset(@offset)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def result
|
30
|
+
@result ||= @scope.where("data LIKE ?", "%#{@query}%")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
module MembershipGroup
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :memberships, as: :group, dependent: :delete_all
|
9
|
+
has_many :users, through: :memberships, source: :external_user, dependent: :delete_all
|
10
|
+
|
11
|
+
delegate :count, to: :users
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/config/routes.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
Audiences::Engine.routes.draw do
|
4
4
|
get "/scim(/*scim_path)" => "scim_proxy#get", as: :scim_proxy
|
5
5
|
get "/:key" => "contexts#show", as: :signed_context
|
6
|
+
get "/:key/users(/:criterion_id)" => "contexts#users", as: :users
|
7
|
+
put "/:key" => "contexts#update"
|
6
8
|
end
|
7
9
|
|
8
10
|
Rails.application.routes.draw do
|
@@ -5,8 +5,9 @@ class CreateAudiencesContexts < ActiveRecord::Migration[6.0]
|
|
5
5
|
create_table :audiences_contexts do |t|
|
6
6
|
t.references :owner, polymorphic: true, null: false, index: { unique: true }
|
7
7
|
t.boolean :match_all, default: false, null: false
|
8
|
+
t.json :criteria
|
8
9
|
|
9
|
-
t.timestamps
|
10
|
+
t.timestamps precision: 0
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesCriterions < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :audiences_criterions do |t|
|
6
|
+
t.json :groups
|
7
|
+
t.references :context, null: false, foreign_key: false
|
8
|
+
|
9
|
+
t.timestamps precision: 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesExternalUsers < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :audiences_external_users do |t|
|
6
|
+
t.string :user_id, null: false
|
7
|
+
t.json :data
|
8
|
+
|
9
|
+
t.timestamps precision: 0
|
10
|
+
t.index :user_id, unique: true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateAudiencesMemberships < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
create_table :audiences_memberships do |t|
|
6
|
+
t.references :external_user, null: false, foreign_key: false
|
7
|
+
t.references :group, null: false, foreign_key: false, polymorphic: true
|
8
|
+
|
9
|
+
t.timestamps precision: 0
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/docs/CHANGELOG.md
CHANGED
data/docs/README.md
CHANGED
@@ -13,30 +13,45 @@ That can be done with a unobstrusive JS renderer like react-rails, or a custom o
|
|
13
13
|
- The context URI: `audience_context_url(owner)` helper
|
14
14
|
- The SCIM endpoint: `audience_scim_proxy_url` helper if using the [proxy](#configuring-the-scim-proxy), or the SCIM endpoint.
|
15
15
|
|
16
|
-
###
|
16
|
+
### Configuring the SCIM backend
|
17
17
|
|
18
|
-
|
18
|
+
The Audience::Scim should point to the real SCIM endpoint. The service allows you to configure the endpoint and the credentials/headers:
|
19
19
|
|
20
|
-
|
20
|
+
I.e.:
|
21
21
|
|
22
|
-
|
22
|
+
```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
|
+
)
|
27
|
+
```
|
23
28
|
|
24
|
-
|
29
|
+
### Listening to audience changes
|
30
|
+
|
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:
|
25
32
|
|
26
33
|
```ruby
|
27
|
-
|
34
|
+
Rails.application.config.to_prepare do
|
35
|
+
Audiences::Notifications.subscribe Team do |context|
|
36
|
+
team.update_memberships(context.users)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
```
|
28
40
|
|
29
|
-
|
41
|
+
or scheduling an AcitiveJob:
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
debug: $stdout,
|
37
|
-
}
|
43
|
+
```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")
|
47
|
+
end
|
38
48
|
```
|
39
49
|
|
50
|
+
You can find a working example in our dummy app:
|
51
|
+
|
52
|
+
- [initializer](../spec/dummy/config/initializers/audiences.rb)
|
53
|
+
- [job class](../spec/dummy/app/jobs/update_memberships_job.rb)
|
54
|
+
|
40
55
|
## Installation
|
41
56
|
|
42
57
|
Add this line to your application's Gemfile:
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Audiences
|
4
|
+
# Handles notification of audience context changes. The notifications handled
|
5
|
+
# by this module are related to the membership composition of a context.
|
6
|
+
#
|
7
|
+
# For instance, when a user leaves a group, the app will be notified of changes
|
8
|
+
# to the members of that context so it can react to that. When the audience
|
9
|
+
# configuration of a context changes, a notification will also be published
|
10
|
+
# through `Audiences::Notifications`.
|
11
|
+
#
|
12
|
+
module Notifications
|
13
|
+
mattr_reader :subscriptions, default: {}
|
14
|
+
|
15
|
+
module_function
|
16
|
+
|
17
|
+
# Subscribes to audience changes to a specific owner type, either with a
|
18
|
+
# background job or a callable block
|
19
|
+
#
|
20
|
+
# @param owner_type [Class] the type of owners handled by the job or block
|
21
|
+
# @param job [Class<ActiveJob::Base>] job that will respond to audience changes
|
22
|
+
# @yield block that will handle the audience change if a job is not given
|
23
|
+
#
|
24
|
+
def subscribe(owner_type, job: nil, &cbk)
|
25
|
+
subscriptions[owner_type] = job&.method(:perform_later) || cbk
|
26
|
+
end
|
27
|
+
|
28
|
+
# Notifies that a given audience context was changed
|
29
|
+
#
|
30
|
+
# @param context [Audiences::Context] updated context
|
31
|
+
#
|
32
|
+
def publish(context)
|
33
|
+
subscriptions[context.owner.class]&.call(context)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "json"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
module Audiences
|
8
|
+
module Scim
|
9
|
+
class Client
|
10
|
+
def initialize(uri:, headers: {})
|
11
|
+
@uri = uri
|
12
|
+
@headers = headers
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform_request(method:, path:, query: {})
|
16
|
+
uri = URI.join(@uri, path.to_s)
|
17
|
+
uri.query = URI.encode_www_form(query)
|
18
|
+
request = ::Net::HTTP.const_get(method).new(uri, @headers)
|
19
|
+
|
20
|
+
http = ::Net::HTTP.new(uri.host, uri.port)
|
21
|
+
http.use_ssl = uri.scheme == "https"
|
22
|
+
|
23
|
+
response = http.request(request)
|
24
|
+
|
25
|
+
JSON.parse(response.body)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "json"
|
5
|
+
require "uri"
|
6
|
+
|
7
|
+
module Audiences
|
8
|
+
module Scim
|
9
|
+
class ResourcesQuery
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
attr_reader :query_options
|
13
|
+
|
14
|
+
def initialize(client, resource_type:, **query_options)
|
15
|
+
@client = client
|
16
|
+
@resource_type = resource_type
|
17
|
+
@query_options = query_options
|
18
|
+
end
|
19
|
+
|
20
|
+
def all
|
21
|
+
to_enum(:each, all: true)
|
22
|
+
end
|
23
|
+
|
24
|
+
def each(all: false, &block)
|
25
|
+
resources.each(&block)
|
26
|
+
next_page&.each(all: true, &block) if all
|
27
|
+
end
|
28
|
+
|
29
|
+
def resources
|
30
|
+
@resources ||= response.fetch("Resources", [])
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_page?
|
34
|
+
start_index = response.fetch("startIndex", 1)
|
35
|
+
per_page = response["itemsPerPage"].to_i
|
36
|
+
total_results = response["totalResults"].to_i
|
37
|
+
|
38
|
+
start_index + per_page <= total_results
|
39
|
+
end
|
40
|
+
|
41
|
+
def next_page
|
42
|
+
return unless next_page?
|
43
|
+
|
44
|
+
current_page = @query_options.fetch(:page, 1)
|
45
|
+
ResourcesQuery.new(@client, resource_type: @resource_type, **@query_options,
|
46
|
+
page: current_page + 1)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def response
|
52
|
+
@response ||= @client.perform_request(path: @resource_type, method: :Get, query: @query_options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "scim/client"
|
4
|
+
require_relative "scim/resources_query"
|
5
|
+
|
6
|
+
module Audiences
|
7
|
+
module Scim
|
8
|
+
mattr_accessor :client
|
9
|
+
mattr_accessor :defaults, default: Hash.new(attributes: "id,displayName")
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
def resources(type:, client: Scim.client, **options)
|
14
|
+
options = defaults.fetch(type, {}).merge(options)
|
15
|
+
|
16
|
+
ResourcesQuery.new(client, resource_type: type, **options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/audiences/version.rb
CHANGED
data/lib/audiences.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "audiences/version"
|
4
|
+
require "audiences/scim"
|
5
|
+
require "audiences/notifications"
|
4
6
|
|
5
7
|
# Audiences system
|
6
8
|
# Audiences pushes notifications to your rails app when a
|
@@ -27,7 +29,33 @@ module_function
|
|
27
29
|
# @return Audience::Context
|
28
30
|
#
|
29
31
|
def load(key)
|
32
|
+
locate_context(key, &:readonly!)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Updates the given context
|
36
|
+
#
|
37
|
+
# Params might contain:
|
38
|
+
#
|
39
|
+
# match_all: Boolean
|
40
|
+
# criteria: Array<{ <group_type>: Array<Integer> }>
|
41
|
+
#
|
42
|
+
# @param token [String] a signed token (see #sign)
|
43
|
+
# @param params [Hash] the updated params
|
44
|
+
# @return Audience::Context
|
45
|
+
#
|
46
|
+
def update(key, criteria: [], **attrs)
|
47
|
+
locate_context(key) do |context|
|
48
|
+
context.update!(
|
49
|
+
criteria: ::Audiences::Criterion.map(criteria),
|
50
|
+
**attrs
|
51
|
+
)
|
52
|
+
context.refresh_users!
|
53
|
+
context.readonly!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private_class_method def locate_context(key, &block)
|
30
58
|
owner = GlobalID::Locator.locate_signed(key, for: GID_RESOURCE)
|
31
|
-
::Audiences::Context.
|
59
|
+
::Audiences::Context.for(owner).tap(&block)
|
32
60
|
end
|
33
61
|
end
|
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:
|
4
|
+
version: 1.0.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:
|
11
|
+
date: 2024-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -39,23 +39,39 @@ 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/
|
42
|
+
- app/models/audiences/context_users.rb
|
43
|
+
- app/models/audiences/criterion.rb
|
44
|
+
- app/models/audiences/criterion_users.rb
|
45
|
+
- app/models/audiences/external_user.rb
|
46
|
+
- app/models/audiences/membership.rb
|
47
|
+
- app/models/audiences/users_search.rb
|
48
|
+
- app/models/concerns/audiences/membership_group.rb
|
43
49
|
- config/routes.rb
|
44
50
|
- db/migrate/20230725194934_create_audiences_contexts.rb
|
51
|
+
- db/migrate/20231114210454_add_extra_users_to_audiences_context.rb
|
52
|
+
- db/migrate/20231114210633_remove_criteria_from_audiences_context.rb
|
53
|
+
- db/migrate/20231114215843_create_audiences_criterions.rb
|
54
|
+
- db/migrate/20231115204932_add_users_to_audiences_criterion.rb
|
55
|
+
- db/migrate/20231130165945_create_audiences_external_users.rb
|
56
|
+
- db/migrate/20231130172718_create_audiences_memberships.rb
|
57
|
+
- db/migrate/20231205140046_remove_serialized_users_from_criterion.rb
|
45
58
|
- docs/CHANGELOG.md
|
46
59
|
- docs/README.md
|
47
60
|
- lib/audiences.rb
|
48
61
|
- lib/audiences/engine.rb
|
49
|
-
- lib/audiences/
|
62
|
+
- lib/audiences/notifications.rb
|
63
|
+
- lib/audiences/scim.rb
|
64
|
+
- lib/audiences/scim/client.rb
|
65
|
+
- lib/audiences/scim/resources_query.rb
|
50
66
|
- lib/audiences/version.rb
|
51
67
|
- lib/tasks/audiences_tasks.rake
|
52
|
-
homepage: https://github.com/powerhome/
|
68
|
+
homepage: https://github.com/powerhome/audiences
|
53
69
|
licenses:
|
54
70
|
- MIT
|
55
71
|
metadata:
|
56
|
-
homepage_uri: https://github.com/powerhome/
|
57
|
-
source_code_uri: https://github.com/powerhome/
|
58
|
-
changelog_uri: https://github.com/powerhome/
|
72
|
+
homepage_uri: https://github.com/powerhome/audiences
|
73
|
+
source_code_uri: https://github.com/powerhome/audiences
|
74
|
+
changelog_uri: https://github.com/powerhome/audiences/blob/main/packages/audiences/docs/CHANGELOG.md
|
59
75
|
rubygems_mfa_required: 'true'
|
60
76
|
post_install_message:
|
61
77
|
rdoc_options: []
|
@@ -72,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
88
|
- !ruby/object:Gem::Version
|
73
89
|
version: '0'
|
74
90
|
requirements: []
|
75
|
-
rubygems_version: 3.
|
91
|
+
rubygems_version: 3.5.9
|
76
92
|
signing_key:
|
77
93
|
specification_version: 4
|
78
94
|
summary: Audiences system
|
data/lib/audiences/scim_proxy.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "net/http"
|
4
|
-
require "uri"
|
5
|
-
|
6
|
-
module Audiences
|
7
|
-
module ScimProxy
|
8
|
-
mattr_accessor :config do
|
9
|
-
{}
|
10
|
-
end
|
11
|
-
|
12
|
-
module_function
|
13
|
-
|
14
|
-
def get(path, query)
|
15
|
-
response = perform_request(path: path, method: :Get, query: query)
|
16
|
-
|
17
|
-
[response.code, response.body]
|
18
|
-
end
|
19
|
-
|
20
|
-
private_class_method def perform_request(method:, path:, query: {})
|
21
|
-
uri = URI.join(config[:uri], path)
|
22
|
-
uri.query = URI.encode_www_form(query)
|
23
|
-
request = ::Net::HTTP.const_get(method).new(uri, config[:headers])
|
24
|
-
|
25
|
-
http = ::Net::HTTP.new(uri.host, uri.port)
|
26
|
-
http.use_ssl = uri.scheme == "https"
|
27
|
-
|
28
|
-
http.request(request)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|