From 897076d391ebc5227f610d33696024b19cdc9c1b Mon Sep 17 00:00:00 2001 From: Akhil Date: Fri, 19 May 2023 18:51:36 +0530 Subject: [PATCH 01/31] Add multi-shop support --- lib/Controller/ShopAccountController.php | 32 +++++---- lib/Service/ShopAccountService.php | 90 ++++++++++++++++-------- 2 files changed, 82 insertions(+), 40 deletions(-) diff --git a/lib/Controller/ShopAccountController.php b/lib/Controller/ShopAccountController.php index b53aa6f5..18a87365 100644 --- a/lib/Controller/ShopAccountController.php +++ b/lib/Controller/ShopAccountController.php @@ -86,7 +86,13 @@ class ShopAccountController extends Controller { if (!$userId) { throw new Exception("Invalid user id"); } - $data = ['order_count' => 0, 'my_orders_url' => $this->shopAccountService->getShopUrl() . '/my-account/orders']; + $data = ['order_count' => 0]; + + $shopUrls = $this->shopAccountService->getShopUrls(); + $data['my_order_urls'] = array_map(function($url) { + return $url . '/my-account/orders'; + }, $shopUrls); + $orders = $this->shopAccountService->getOrders($userId); $data['order_count'] = count($orders); $response = new DataResponse(); @@ -109,13 +115,10 @@ class ShopAccountController extends Controller { } $data = ['subscription_count' => 0]; $subscriptions = $this->shopAccountService->getSubscriptions($userId, 'any'); - $total_subscriptions = 0; - foreach ($subscriptions as $subscription) { - if (in_array($subscription['status'], self::SUBSCRIPTION_STATUS_LIST)) { - $total_subscriptions++; - } - } - $data['subscription_count'] = $total_subscriptions; + $subscriptions = array_filter($subscriptions, function($subscription) { + return in_array($subscription['status'], self::SUBSCRIPTION_STATUS_LIST); + }); + $data['subscription_count'] = count($subscriptions);; $response = new DataResponse(); $response->setData($data); return $response; @@ -129,18 +132,23 @@ class ShopAccountController extends Controller { /** * @NoAdminRequired */ - public function getShopUser() { + public function getShopUsers() { $response = new DataResponse(); $user = $this->userSession->getUser(); $email = $user->getEMailAddress(); - $shopUser = $this->shopAccountService->getUser($email); + $shopUsers = $this->shopAccountService->getUsers($email); - if (!$shopUser || !$this->shopAccountService->isUserOIDC($shopUser)) { + $shopUsers = array_filter($shopUsers, function($shopUser) { + return $this->shopAccountService->isUserOIDC($shopUser); + }); + + if (empty($shopUsers)) { $response->setStatus(404); return $response; } - $response->setData($shopUser); + + $response->setData($shopUsers); return $response; } } diff --git a/lib/Service/ShopAccountService.php b/lib/Service/ShopAccountService.php index 113109b8..4ae89946 100644 --- a/lib/Service/ShopAccountService.php +++ b/lib/Service/ShopAccountService.php @@ -7,17 +7,27 @@ use Exception; use OCP\IConfig; use OCP\ILogger; use OCA\EcloudAccounts\AppInfo\Application; +use OCA\EcloudAccounts\Service\CurlService; class ShopAccountService { - private $config; - private $appName; - private $curl; - private $logger; + private IConfig $config; + private string $appName; + private CurlService $curl; + private ILogger $logger; + private array $shops = []; + + private const ORDERS_ENDPOINT = "/wp-json/wc/v3/orders"; + private const USERS_ENDPOINT = "/wp-json/wp/v2/users"; + private const SUBSCRIPTIONS_ENDPOINT = "/wp-json/wc/v3/subscriptions"; public function __construct($appName, IConfig $config, CurlService $curlService, ILogger $logger) { $this->config = $config; $this->appName = $appName; + $shops = $this->config->getSystemValue('murena_shops', []); + foreach ($shops as $shop) { + $this->shops[$shop['url']] = $shop; + } $shopUsername = $this->config->getSystemValue('murena_shop_username'); $shopPassword = $this->config->getSystemValue('murena_shop_password'); $this->shopUrl = $this->config->getSystemValue('murena_shop_url', ''); @@ -32,8 +42,10 @@ class ShopAccountService { $this->logger = $logger; } - public function getShopUrl() { - return $this->shopUrl; + public function getShopUrls() : array { + return array_map(function($shop) { + return $shop['url']; + }, $this->shops); } public function setShopDeletePreference($userId, bool $delete) { @@ -71,12 +83,25 @@ class ShopAccountService { } public function getOrders(int $userId): ?array { - return $this->callShopAPI($this->shopOrdersUrl, 'GET', ['customer' => $userId]); + $orders = []; + foreach ($this->shops as $shop) { + $orders[] = $this->callShopAPI($shop, self::ORDERS_ENDPOINT, 'GET', ['customer' => $userId]); + } + return $orders; } public function getUsers(string $searchTerm): ?array { try { - return $this->callShopAPI($this->shopUserUrl, 'GET', ['search' => $searchTerm]); + $users = []; + foreach ($this->shops as $shop) { + $usersFromThisShop = $this->callShopAPI($shop, self::USERS_ENDPOINT, 'GET', ['search' => $searchTerm]); + foreach ($usersFromThisShop as $user) { + $user['shop_url'] = $shop['url']; + } + $users[] = $usersFromThisShop; + } + + return $users; } catch (Exception $e) { $this->logger->error('There was an issue querying shop for users'); $this->logger->logException($e, ['app' => Application::APP_ID]); @@ -91,15 +116,16 @@ class ShopAccountService { return $users[0]; } - public function deleteUser(int $userId) : void { + public function deleteUser(string $shopUrl, int $userId) : void { + $shop = $this->shops[$shopUrl]; $params = [ 'force' => true, - 'reassign' => $this->shopReassignUserId + 'reassign' => $shop['reassign_user_id'] ]; - $deleteUrl = $this->shopUserUrl . '/' . strval($userId); + $deleteEndpoint = self::USERS_ENDPOINT . '/' . strval($userId); try { - $answer = $this->callShopAPI($deleteUrl, 'DELETE', $params); + $answer = $this->callShopAPI($shop, $deleteEndpoint, 'DELETE', $params); if (!$answer['deleted']) { throw new Exception('Unknown error while deleting!'); @@ -110,8 +136,9 @@ class ShopAccountService { } } - public function updateUserEmailAndEmptyOIDC(int $userId, string $email) : void { - $updateUrl = $this->shopUserUrl . '/' . strval($userId); + public function updateUserEmailAndEmptyOIDC(string $shopUrl, int $userId, string $email) : void { + $shop = $this->shops[$shopUrl]; + $updateEndpoint = self::USERS_ENDPOINT . '/' . strval($userId); $params = [ 'email' => $email, @@ -119,7 +146,7 @@ class ShopAccountService { ]; try { - $answer = $this->callShopAPI($updateUrl, 'POST', $params); + $answer = $this->callShopAPI($shop, $updateEndpoint, 'POST', $params); if ($answer['email'] !== $email) { throw new Exception('Unknown error while updating!'); @@ -130,26 +157,39 @@ class ShopAccountService { } } - private function callShopAPI(string $url, string $method, array $data = []) { - if (empty($this->shopUrl)) { + public function isUserOIDC(array $user) { + return !empty($user['openid-connect-generic-last-user-claim']); + } + + public function getSubscriptions(int $userId, string $status): ?array { + $subscriptions = []; + foreach ($this->shops as $shop) { + $subscriptions[] = $this->callShopAPI($shop, self::SUBSCRIPTIONS_ENDPOINT, 'GET', ['customer' => $userId , 'status' => $status]); + } + return $subscriptions; + } + + private function callShopAPI(array $shop, string $endpoint, string $method, array $data = []) { + if (empty($shop['url'])) { return []; } + $shopCredentials = $shop['username'] . ':' . $shop['password']; $headers = [ "cache-control: no-cache", "content-type: application/json", - "Authorization: Basic " . $this->shopCredentials + "Authorization: Basic " . $shopCredentials ]; if ($method === 'GET') { - $answer = $this->curl->get($url, $data, $headers); + $answer = $this->curl->get($endpoint, $data, $headers); } if ($method === 'DELETE') { - $answer = $this->curl->delete($url, $data, $headers); + $answer = $this->curl->delete($endpoint, $data, $headers); } if ($method === 'POST') { - $answer = $this->curl->post($url, json_encode($data), $headers); + $answer = $this->curl->post($endpoint, json_encode($data), $headers); } $answer = json_decode($answer, true); @@ -160,11 +200,5 @@ class ShopAccountService { return $answer; } - public function isUserOIDC(array $user) { - return !empty($user['openid-connect-generic-last-user-claim']); - } - - public function getSubscriptions(int $userId, string $status): ?array { - return $this->callShopAPI($this->subscriptionUrl, 'GET', ['customer' => $userId , 'status' => $status]); - } + } -- GitLab From 4b6bd86184d4fb9bf0740d4fc3547338683a1a85 Mon Sep 17 00:00:00 2001 From: Akhil Date: Tue, 23 May 2023 23:13:11 +0530 Subject: [PATCH 02/31] Refactor assuming API returns subscription and order info --- appinfo/info.xml | 2 +- lib/Service/ShopAccountService.php | 26 +++-- ...sonal.php => DeleteShopAccountSetting.php} | 4 +- src/PersonalSettings.vue | 94 ++++++------------- 4 files changed, 49 insertions(+), 77 deletions(-) rename lib/Settings/{Personal.php => DeleteShopAccountSetting.php} (93%) diff --git a/appinfo/info.xml b/appinfo/info.xml index dc5aa25c..eb1cb5ef 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -20,7 +20,7 @@ - OCA\EcloudAccounts\Settings\Personal + OCA\EcloudAccounts\Settings\DeleteShopAccountSetting OCA\EcloudAccounts\Settings\BetaUserSetting OCA\EcloudAccounts\Settings\BetaSection diff --git a/lib/Service/ShopAccountService.php b/lib/Service/ShopAccountService.php index 4ae89946..8145a809 100644 --- a/lib/Service/ShopAccountService.php +++ b/lib/Service/ShopAccountService.php @@ -16,9 +16,15 @@ class ShopAccountService { private ILogger $logger; private array $shops = []; - private const ORDERS_ENDPOINT = "/wp-json/wc/v3/orders"; - private const USERS_ENDPOINT = "/wp-json/wp/v2/users"; - private const SUBSCRIPTIONS_ENDPOINT = "/wp-json/wc/v3/subscriptions"; + private const ORDERS_ENDPOINT = '/wp-json/wc/v3/orders'; + private const USERS_ENDPOINT = '/wp-json/wp/v2/users'; + private const SUBSCRIPTIONS_ENDPOINT = '/wp-json/wc/v3/subscriptions'; + private const MY_ORDERS_ENDPOINT = '/my-account/orders'; + private const SUBSCRIPTION_STATUS_LIST = [ + 'pending', + 'active', + 'on-hold' + ]; public function __construct($appName, IConfig $config, CurlService $curlService, ILogger $logger) { $this->config = $config; @@ -95,10 +101,10 @@ class ShopAccountService { $users = []; foreach ($this->shops as $shop) { $usersFromThisShop = $this->callShopAPI($shop, self::USERS_ENDPOINT, 'GET', ['search' => $searchTerm]); - foreach ($usersFromThisShop as $user) { - $user['shop_url'] = $shop['url']; + if (empty($usersFromThisShop)) { + continue; } - $users[] = $usersFromThisShop; + $users[] = $usersFromThisShop[0]; } return $users; @@ -161,7 +167,7 @@ class ShopAccountService { return !empty($user['openid-connect-generic-last-user-claim']); } - public function getSubscriptions(int $userId, string $status): ?array { + public function getSubscriptions(int $userId, string $status = 'any'): ?array { $subscriptions = []; foreach ($this->shops as $shop) { $subscriptions[] = $this->callShopAPI($shop, self::SUBSCRIPTIONS_ENDPOINT, 'GET', ['customer' => $userId , 'status' => $status]); @@ -181,15 +187,15 @@ class ShopAccountService { ]; if ($method === 'GET') { - $answer = $this->curl->get($endpoint, $data, $headers); + $answer = $this->curl->get($shop['url'] . $endpoint, $data, $headers); } if ($method === 'DELETE') { - $answer = $this->curl->delete($endpoint, $data, $headers); + $answer = $this->curl->delete($shop['url'] . $endpoint, $data, $headers); } if ($method === 'POST') { - $answer = $this->curl->post($endpoint, json_encode($data), $headers); + $answer = $this->curl->post($shop['url'] . $endpoint, json_encode($data), $headers); } $answer = json_decode($answer, true); diff --git a/lib/Settings/Personal.php b/lib/Settings/DeleteShopAccountSetting.php similarity index 93% rename from lib/Settings/Personal.php rename to lib/Settings/DeleteShopAccountSetting.php index 2e694e98..f94db16c 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/DeleteShopAccountSetting.php @@ -12,7 +12,7 @@ use OCA\EcloudAccounts\Service\ShopAccountService; use OCP\App\IAppManager; use OCP\IUserManager; -class Personal implements ISettings { +class DeleteShopAccountSetting implements ISettings { private const DROP_ACCOUNT_APP_ID = 'drop_account'; /** @var IUserSession */ private $userSession; @@ -65,6 +65,8 @@ class Personal implements ISettings { $this->initialState->provideInitialState('shop_email_post_delete', $shopEmailPostDelete); $this->initialState->provideInitialState('only_user', $onlyUser); $this->initialState->provideInitialState('only_admin', $onlyAdmin); + $shopUsers = $this->shopAccountService->getUsers($user->getEMailAddress()); + $this->initialState->provideInitialState('shopUsers', $shopUsers); } return new TemplateResponse($this->appName, 'personal'); diff --git a/src/PersonalSettings.vue b/src/PersonalSettings.vue index 69ad36f9..9444a25e 100644 --- a/src/PersonalSettings.vue +++ b/src/PersonalSettings.vue @@ -1,11 +1,6 @@ @@ -76,32 +64,34 @@ import SettingsSection from '@nextcloud/vue/dist/Components/SettingsSection.js' import Axios from '@nextcloud/axios' import { generateUrl } from '@nextcloud/router' import { showError } from '@nextcloud/dialogs' +import ShopUserOrders from './components/ShopUserOrders.vue' const APPLICATION_NAME = 'ecloud-accounts' export default { - name: 'PersonalSettings', + name: 'DeleteShopAccountSetting', components: { SettingsSection, + ShopUserOrders, }, data() { return { - shopUsers: loadState(APPLICATION_NAME, 'shopUsers'), + shopUsers: [], deleteShopAccount: loadState(APPLICATION_NAME, 'delete_shop_account'), shopEmailPostDelete: loadState(APPLICATION_NAME, 'shop_email_post_delete'), shopEmailDefault: loadState(APPLICATION_NAME, 'shop_email_post_delete'), appName: APPLICATION_NAME, userEmail: loadState(APPLICATION_NAME, 'email'), - onlyAdmin: loadState(APPLICATION_NAME, 'only_admin'), - onlyUser: loadState(APPLICATION_NAME, 'only_user'), orderCount: 0, subscriptionCount: 0, - ordersDescription: this.t(APPLICATION_NAME, "For your information you have %d order(s) in your account."), - subscriptionDescription: this.t(APPLICATION_NAME, 'A subscription is active in this account. Please cancel it or let it expire before deleting your account.'), + loading: true, showError: false, allowDelete: true, } }, + mounted() { + this.getShopUsers() + }, created() { this.disableOrEnableDeleteAccount() }, @@ -148,14 +138,14 @@ export default { return err.response.status } }, - async getShopUser() { + async getShopUsers() { try { const url = generateUrl( - `/apps/${this.appName}/shop-accounts/user` + `/apps/${this.appName}/shop-accounts/users` ) const { status, data } = await Axios.get(url) if (status === 200) { - this.shopUser = data + this.shopUsers = data } if (status === 400) { this.enableDeleteAccountEvent() diff --git a/src/components/ShopUserOrders.vue b/src/components/ShopUserOrders.vue new file mode 100644 index 00000000..83c0e9b4 --- /dev/null +++ b/src/components/ShopUserOrders.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/personal.js b/src/delete-shop-account.js similarity index 69% rename from src/personal.js rename to src/delete-shop-account.js index ad8127b0..4ab00df5 100644 --- a/src/personal.js +++ b/src/delete-shop-account.js @@ -1,6 +1,6 @@ import Vue from 'vue' import './common.js' -import PersonalSettings from './PersonalSettings.vue' +import PersonalSettings from './DeleteShopAccountSetting.vue' export default new Vue({ el: '#ecloud-accounts-settings', diff --git a/webpack.config.js b/webpack.config.js index ea2fb2f2..0868492d 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,7 +6,7 @@ const path = require('path') module.exports = { ...webpackConfig, entry: { - 'personal-settings': path.join(__dirname, 'src/personal.js'), + 'delete-shop-account-settings': path.join(__dirname, 'src/delete-shop-account.js'), 'delete-account-listeners': path.join(__dirname, 'src/delete-account-listeners.js'), 'beta-user-setting': path.join(__dirname, 'src/beta-user-setting.js'), }, -- GitLab From 68bbbf47c2348d593e3916de604e469d44ae53f7 Mon Sep 17 00:00:00 2001 From: Akhil Date: Mon, 29 May 2023 18:04:09 +0530 Subject: [PATCH 04/31] Refactor appname --- src/DeleteShopAccountSetting.vue | 79 +++++++++++++------------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/src/DeleteShopAccountSetting.vue b/src/DeleteShopAccountSetting.vue index ebfbe26f..53cf7e1c 100644 --- a/src/DeleteShopAccountSetting.vue +++ b/src/DeleteShopAccountSetting.vue @@ -1,13 +1,13 @@