From 5fb3ff792074c61ae565ee6b384e2b49af4e6c6a Mon Sep 17 00:00:00 2001 From: Lukasz Grabowski <34243011+lucgrabowski@users.noreply.github.com> Date: Mon, 2 Aug 2021 22:43:16 +0200 Subject: [PATCH] [spaceship] Add visible_apps relationship to invite users with app permissions and fetch user's app permissions (#19053) * Add visibleApps to User and UserInvitation * Use visible_apps in create UserInvitation * Use visible_apps in post_user_invitation * Get visible apps for invited user * Use visible_apps in post_user_invitation - fix to use relationships * Rename visible_apps param to visible_app_ids * Explain how to use all_apps_visible and visible_app_ids to create user invitation * Fetch visible apps response fix * Use all_pages in fetch_visible_apps * Fetch visible apps for user * Add tests for get requests, build_params for calls * Add limit param for get requests * Add test for post_user_invitation * Rubocop fixes * Add limit param to fetch_visible_apps methods * Code review improvements - rename fetch to get * Code review improvements - get essential includes for user and user_invitation --- .../lib/spaceship/connect_api/models/user.rb | 20 ++++- .../connect_api/models/user_invitation.rb | 31 ++++++-- .../lib/spaceship/connect_api/users/users.rb | 26 ++++++- .../connect_api/users/user_client_spec.rb | 75 +++++++++++++++++++ 4 files changed, 142 insertions(+), 10 deletions(-) diff --git a/spaceship/lib/spaceship/connect_api/models/user.rb b/spaceship/lib/spaceship/connect_api/models/user.rb index 16c47a8ee2c..905848c8062 100644 --- a/spaceship/lib/spaceship/connect_api/models/user.rb +++ b/spaceship/lib/spaceship/connect_api/models/user.rb @@ -16,6 +16,8 @@ class User attr_accessor :email_vetting_required attr_accessor :notifications + attr_accessor :visible_apps + attr_mapping({ "username" => "username", "firstName" => "first_name", @@ -27,9 +29,15 @@ class User "allAppsVisible" => "all_apps_visible", "provisioningAllowed" => "provisioning_allowed", "emailVettingRequired" => "email_vetting_required", - "notifications" => "notifications" + "notifications" => "notifications", + + "visibleApps" => "visible_apps" }) + ESSENTIAL_INCLUDES = [ + "visibleApps" + ].join(",") + def self.type return "users" end @@ -38,16 +46,22 @@ def self.type # API # - def self.all(client: nil, filter: {}, includes: nil, limit: nil, sort: nil) + def self.all(client: nil, filter: {}, includes: ESSENTIAL_INCLUDES, limit: nil, sort: nil) client ||= Spaceship::ConnectAPI resps = client.get_users(filter: filter, includes: includes).all_pages return resps.flat_map(&:to_models) end - def self.find(client: nil, email: nil, includes: nil) + def self.find(client: nil, email: nil, includes: ESSENTIAL_INCLUDES) client ||= Spaceship::ConnectAPI return all(client: client, filter: { email: email }, includes: includes) end + + def get_visible_apps(client: nil, limit: nil) + client ||= Spaceship::ConnectAPI + resp = client.get_user_visible_apps(user_id: id, limit: limit) + return resp.to_models + end end end end diff --git a/spaceship/lib/spaceship/connect_api/models/user_invitation.rb b/spaceship/lib/spaceship/connect_api/models/user_invitation.rb index 5e9f609fa45..d7bd539a542 100644 --- a/spaceship/lib/spaceship/connect_api/models/user_invitation.rb +++ b/spaceship/lib/spaceship/connect_api/models/user_invitation.rb @@ -11,15 +11,23 @@ class UserInvitation attr_accessor :all_apps_visible attr_accessor :provisioning_allowed + attr_accessor :visible_apps + attr_mapping({ "firstName" => "first_name", "lastName" => "last_name", "email" => "email", "roles" => "roles", "allAppsVisible" => "all_apps_visible", - "provisioningAllowed" => "provisioning_allowed" + "provisioningAllowed" => "provisioning_allowed", + + "visibleApps" => "visible_apps" }) + ESSENTIAL_INCLUDES = [ + "visibleApps" + ].join(",") + module UserRole ADMIN = "ADMIN" FINANCE = "FINANCE" @@ -42,18 +50,23 @@ def self.type # Managing invitations # - def self.all(client: nil, filter: {}, includes: nil, sort: nil) + def self.all(client: nil, filter: {}, includes: ESSENTIAL_INCLUDES, sort: nil) client ||= Spaceship::ConnectAPI resps = client.get_user_invitations(filter: filter, includes: includes, sort: sort).all_pages return resps.flat_map(&:to_models) end - def self.find(client: nil, email: nil, includes: nil) + def self.find(client: nil, email: nil, includes: ESSENTIAL_INCLUDES) client ||= Spaceship::ConnectAPI return all(client: client, filter: { email: email }, includes: includes) end - def self.create(client: nil, email: nil, first_name: nil, last_name: nil, roles: [], provisioning_allowed: nil, all_apps_visible: nil) + # Create and post user invitation + # App Store Connect allows for the following combinations of `all_apps_visible` and `visible_app_ids`: + # - if `all_apps_visible` is `nil`, you don't have to provide values for `visible_app_ids` + # - if `all_apps_visible` is true, you must provide values for `visible_app_ids`. + # - if `all_apps_visible` is false, you must not provide values for `visible_app_ids`. + def self.create(client: nil, email: nil, first_name: nil, last_name: nil, roles: [], provisioning_allowed: nil, all_apps_visible: nil, visible_app_ids: []) client ||= Spaceship::ConnectAPI resp = client.post_user_invitation( email: email, @@ -61,7 +74,8 @@ def self.create(client: nil, email: nil, first_name: nil, last_name: nil, roles: last_name: last_name, roles: roles, provisioning_allowed: provisioning_allowed, - all_apps_visible: all_apps_visible + all_apps_visible: all_apps_visible, + visible_app_ids: visible_app_ids ) return resp.to_models.first end @@ -70,6 +84,13 @@ def delete!(client: nil) client ||= Spaceship::ConnectAPI client.delete_user_invitation(user_invitation_id: id) end + + # Get visible apps for invited user + def get_visible_apps(client: nil, limit: nil) + client ||= Spaceship::ConnectAPI + resp = client.get_user_invitation_visible_apps(user_invitation_id: id, limit: limit) + return resp.to_models + end end end end diff --git a/spaceship/lib/spaceship/connect_api/users/users.rb b/spaceship/lib/spaceship/connect_api/users/users.rb index b088ebc49a9..52c14e4d1d6 100644 --- a/spaceship/lib/spaceship/connect_api/users/users.rb +++ b/spaceship/lib/spaceship/connect_api/users/users.rb @@ -42,18 +42,24 @@ def add_user_visible_apps(user_id: nil, app_ids: nil) users_request_client.post("users/#{user_id}/relationships/visibleApps", body) end + # Get app permissions for user + def get_user_visible_apps(user_id: id, limit: nil) + params = users_request_client.build_params(filter: {}, includes: nil, limit: limit, sort: nil) + users_request_client.get("users/#{user_id}/visibleApps", params) + end + # # invitations (invited users) # - # Get all invited users (not yet accepted) + # Get all invited users def get_user_invitations(filter: {}, includes: nil, limit: nil, sort: nil) params = users_request_client.build_params(filter: filter, includes: includes, limit: limit, sort: sort) users_request_client.get("userInvitations", params) end # Invite new users to App Store Connect - def post_user_invitation(email: nil, first_name: nil, last_name: nil, roles: [], provisioning_allowed: nil, all_apps_visible: nil) + def post_user_invitation(email: nil, first_name: nil, last_name: nil, roles: [], provisioning_allowed: nil, all_apps_visible: nil, visible_app_ids: []) body = { data: { type: "userInvitations", @@ -64,6 +70,16 @@ def post_user_invitation(email: nil, first_name: nil, last_name: nil, roles: [], roles: roles, provisioningAllowed: provisioning_allowed, allAppsVisible: all_apps_visible + }, + relationships: { + visibleApps: { + data: visible_app_ids.map do |id| + { + id: id, + type: "apps" + } + end + } } } } @@ -74,6 +90,12 @@ def post_user_invitation(email: nil, first_name: nil, last_name: nil, roles: [], def delete_user_invitation(user_invitation_id: nil) users_request_client.delete("userInvitations/#{user_invitation_id}") end + + # Get all app permissions for invited user + def get_user_invitation_visible_apps(user_invitation_id: id, limit: nil) + params = users_request_client.build_params(filter: {}, includes: nil, limit: limit, sort: nil) + users_request_client.get("userInvitations/#{user_invitation_id}/visibleApps", params) + end end end end diff --git a/spaceship/spec/connect_api/users/user_client_spec.rb b/spaceship/spec/connect_api/users/user_client_spec.rb index 26e1f4ea76f..21b50d9a42d 100644 --- a/spaceship/spec/connect_api/users/user_client_spec.rb +++ b/spaceship/spec/connect_api/users/user_client_spec.rb @@ -56,6 +56,81 @@ def test_request_body(url, body) client.get_users end end + + context 'get_user_visible_apps' do + let(:user_id) { "42" } + let(:path) { "users/#{user_id}/visibleApps" } + + it 'succeeds' do + params = {} + req_mock = test_request_params(path, params) + expect(client).to receive(:request).with(:get).and_yield(req_mock).and_return(req_mock) + client.get_user_visible_apps(user_id: user_id) + end + end + end + + describe "user_invitations" do + context 'post_user_invitation' do + let(:path) { "userInvitations" } + let(:attributes) { + { + email: "test@example.com", + firstName: "Firstname", + lastName: "Lastname", + roles: [], + provisioningAllowed: true, + allAppsVisible: false + } + } + let(:visible_app_ids) { ["123", "456"] } + let(:body) do + { + data: { + type: "userInvitations", + attributes: attributes, + relationships: { + visibleApps: { + data: visible_app_ids.map do |id| + { + id: id, + type: "apps" + } + end + } + } + } + } + end + + it 'succeeds' do + url = path + req_mock = test_request_body(url, body) + + expect(client).to receive(:request).with(:post).and_yield(req_mock).and_return(req_mock) + client.post_user_invitation( + email: "test@example.com", + first_name: "Firstname", + last_name: "Lastname", + roles: [], + provisioning_allowed: true, + all_apps_visible: false, + visible_app_ids: ["123", "456"] + ) + end + end + + context 'get_user_invitation_visible_apps' do + let(:invitation_id) { "42" } + let(:path) { "userInvitations/#{invitation_id}/visibleApps" } + + it 'succeeds' do + params = {} + req_mock = test_request_params(path, params) + expect(client).to receive(:request).with(:get).and_yield(req_mock).and_return(req_mock) + client.get_user_invitation_visible_apps(user_invitation_id: invitation_id) + end + end end end end