circles/lib/Command/CirclesTest.php
Maxence Lange 6b8208dbf8 fix
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2021-05-03 11:33:48 -01:00

1148 lines
32 KiB
PHP

<?php
declare(strict_types=1);
/**
* Circles - Bring cloud-users closer together.
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2021
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Circles\Command;
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
use daita\MySmallPhpTools\Exceptions\ItemNotFoundException;
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Deserialize;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use Exception;
use OC\Core\Command\Base;
use OCA\Circles\AppInfo\Application;
use OCA\Circles\Db\CoreQueryBuilder;
use OCA\Circles\Exceptions\CircleNotFoundException;
use OCA\Circles\Model\Circle;
use OCA\Circles\Model\FederatedUser;
use OCA\Circles\Model\Member;
use OCA\Circles\Service\ConfigService;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
/**
* Class CirclesTest
*
* @package OCA\Circles\Command
*/
class CirclesTest extends Base {
use TArrayTools;
use TStringTools;
use TNC22Deserialize;
static $INSTANCES = [
'global-scale-1',
'global-scale-2',
'global-scale-3',
'passive',
'external',
'trusted'
];
static $TEST_CIRCLES = [
'test_001'
];
/** @var CoreQueryBuilder */
private $coreQueryBuilder;
/** @var ConfigService */
private $configService;
/** @var InputInterface */
private $input;
/** @var OutputInterface */
private $output;
/** @var array */
private $config = [];
/** @var string */
private $local = '';
/** @var bool */
private $pOn = false;
/** @var array */
private $circles = [];
/** @var array */
private $federatedUsers = [];
/**
* CirclesTest constructor.
*
* @param CoreQueryBuilder $coreQueryBuilder
* @param ConfigService $configService
*/
public function __construct(CoreQueryBuilder $coreQueryBuilder, ConfigService $configService) {
parent::__construct();
$this->coreQueryBuilder = $coreQueryBuilder;
$this->configService = $configService;
}
/**
*
*/
protected function configure() {
parent::configure();
$this->setName('circles:test')
->setDescription('testing some features')
->addArgument('deprecated', InputArgument::OPTIONAL, '')
->addOption(
'are-you-aware-this-will-delete-all-my-data', '', InputOption::VALUE_REQUIRED,
'Well, are you ?', ''
)
->addOption('skip-init', '', InputOption::VALUE_NONE, 'Bypass Initialisation')
->addOption('skip-setup', '', InputOption::VALUE_NONE, 'Bypass Circles Setup')
->addOption('only-setup', '', InputOption::VALUE_NONE, 'Stop after Circles Setup, pre-Sync');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->input = $input;
$this->output = $output;
if ($input->getOption('are-you-aware-this-will-delete-all-my-data') === 'yes-i-am') {
try {
$this->testCirclesApp();
} catch (Exception $e) {
if ($this->pOn) {
$message = ($e->getMessage() !== '') ? $e->getMessage() : get_class($e);
$this->output->writeln('<error>' . $message . '</error>');
} else {
throw $e;
}
}
return 0;
}
$output->writeln('');
$output->writeln(
'<error>Since Nextcloud 22, this command have changed, please read the message below:</error>'
);
$output->writeln('<error>This new command is to test the integrity of the Circles App.</error>');
$output->writeln(
'<error>Running this command will REMOVE all your current configuration and all your current Circles.</error>'
);
$output->writeln('<error>There is a huge probability that you do not want to do that.</error>');
$output->writeln('');
$output->writeln(
'<error>The old testing command you might looking for have moved to "./occ circles:check"</error>'
);
$output->writeln('');
return 0;
}
/**
* @throws CircleNotFoundException
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function testCirclesApp() {
$this->t('Bootup');
$this->loadConfiguration();
if (!$this->input->getOption('skip-setup')) {
if (!$this->input->getOption('skip-init')) {
$this->t('Initialisation Nextcloud');
$this->initEnvironment();
}
$this->t('Initialisation Circles App');
$this->reloadCirclesApp();
$this->configureCirclesApp();
$this->confirmVersion();
$this->confirmEmptyCircles();
if ($this->input->getOption('only-setup')) {
return;
}
$this->syncCircles();
$this->t('Fresh installation status');
$this->statusFreshInstances();
$this->createRemoteLink();
}
$this->t('Building Local Database');
$this->buildingLocalDatabase();
$this->t('Testing Basic Circle Creation');
$this->circleCreation001();
$this->t('Adding local users and moderators');
$this->addLocalMemberByUserId();
// $this->addLocalMemberBySingleId();
// $this->addLocalMemberUsingMember();
// $this->levelLocalMemberToModerator();
// $this->addRemoteMemberUsingModerator();
// $this->addRemoteMemberUsingRemoteMember();
// $this->levelRemoteMemberToAdmin();
// $this->addRemoteMemberUsingRemoteAdmin();
//
}
/**
* @throws ItemNotFoundException
*/
private function loadConfiguration() {
$this->p('Loading configuration');
$configuration = file_get_contents(__DIR__ . '/../../testConfiguration.json');
$this->config = json_decode($configuration, true);
$this->r(true, 'testConfiguration.json');
$this->p('Checking configuration');
foreach (self::$INSTANCES as $instance) {
$cloudId = $this->getConfig($instance, 'config.frontal_cloud_id');
if ($this->configService->isLocalInstance($cloudId)) {
$this->local = $instance;
}
}
$this->r();
$this->p('Checking local');
if ($this->local === '') {
throw new ItemNotFoundException('local not defined');
}
$this->r(true, $this->local);
}
/**
* @throws ItemNotFoundException
*/
private function initEnvironment() {
$this->p('Disabling Password Policy App:');
foreach ($this->getInstances() as $instanceId) {
$this->occ($instanceId, 'app:disable password_policy', false, false);
$this->pm($instanceId);
}
$this->r();
foreach ($this->getInstances() as $instance) {
$this->p('Creating users on ' . $instance);
foreach ($this->getConfigArray($instance, 'users') as $userId) {
$this->pm($userId);
$this->occ(
$instance, 'user:add --password-from-env ' . $userId, false, false,
['OC_PASS' => 'testtest']
);
}
$this->r();
foreach ($this->getConfigArray($instance, 'groups') as $groupId => $users) {
$this->p('Creating group <info>' . $groupId . '</info> on <info>' . $instance . '</info>');
$this->occ($instance, 'group:add ' . $groupId, false, false);
foreach ($users as $userId) {
$this->pm($userId);
$this->occ($instance, 'group:adduser ' . $groupId . ' ' . $userId, true, false);
}
$this->r();
}
}
}
/**
* @throws ItemNotFoundException
*/
private function reloadCirclesApp() {
$this->p('Reload Circles App');
foreach ($this->getInstances(false) as $instance) {
$this->pm($instance);
$this->occ($instance, 'circles:clean --uninstall', false, false);
$this->occ($instance, 'app:enable circles', true, false);
}
$this->r();
$this->p('Empty Circles database on local');
$this->coreQueryBuilder->cleanDatabase();
$this->r();
}
/**
* @throws ItemNotFoundException
*/
private function configureCirclesApp() {
$this->p('Configure Circles App');
foreach ($this->getInstances(true) as $instance) {
$this->pm($instance);
foreach ($this->getConfigArray($instance, 'config') as $k => $v) {
$this->occ($instance, 'config:app:set --value ' . $v . ' circles ' . $k, true, false);
}
}
$this->r();
}
/**
* @throws ItemNotFoundException
* @throws Exception
*/
private function confirmVersion() {
$version = $this->configService->getAppValue('installed_version');
$this->p('Confirming version <info>' . $version . '</info>');
foreach ($this->getInstances(false) as $instance) {
$this->pm($instance);
$capabilities = $this->occ($instance, 'circles:check --capabilities');
$v = $this->get('version', $capabilities);
if ($v !== $version) {
throw new Exception($v);
}
}
$this->r();
}
/**
* @throws ItemNotFoundException
* @throws Exception
*/
private function confirmEmptyCircles() {
$this->p('Confirming empty database');
foreach ($this->getInstances() as $instance) {
$this->pm($instance);
$result = $this->occ($instance, 'circles:manage:list --all');
if (!is_array($result) || !empty($result)) {
throw new Exception('no');
}
}
$this->r();
}
/**
* @throws ItemNotFoundException
* @throws Exception
*/
private function syncCircles() {
$this->p('Running Circles Sync');
foreach ($this->getInstances() as $instance) {
$this->pm($instance);
$this->occ($instance, 'circles:sync');
}
$this->r();
}
/**
* @throws CircleNotFoundException
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function statusFreshInstances() {
foreach ($this->getInstances() as $instanceId) {
$this->p('Circles on ' . $instanceId);
$result = $this->occ($instanceId, 'circles:manage:list --all');
$expectedSize = sizeof($this->getConfigArray($instanceId, 'groups'))
+ sizeof($this->getConfigArray($instanceId, 'users'))
+ 1;
$this->r((sizeof($result) === $expectedSize), sizeof($result) . ' circles');
$membersList = $groupsList = [];
foreach ($result as $item) {
/** @var Circle $circle */
$circle = $this->deserialize($item, Circle::class);
if ($circle->isConfig(Circle::CFG_SINGLE)) {
$membersList[] = $circle;
}
if ($circle->getSource() === Member::TYPE_GROUP) {
$groupsList[] = $circle;
}
}
$instance = $this->getConfig($instanceId, 'config.frontal_cloud_id');
foreach ($this->getConfigArray($instanceId, 'users') as $userId) {
$this->p('Checking Single Circle for <comment>' . $userId . '@' . $instance . '</comment>');
$circle = $this->getSingleCircleForMember($membersList, $userId, $instance);
$compareToOwnerBasedOn = new Circle();
$compareToOwnerBasedOn->setConfig(Circle::CFG_SINGLE)
->setName('user:' . $userId . ':{CIRCLEID}')
->setDisplayName($userId);
$compareToOwner = new Member();
$compareToOwner->setUserId($userId)
->setUserType(Member::TYPE_USER)
->setInstance($instance)
->setDisplayName($userId)
->setId('{CIRCLEID}')
->setCircleId('{CIRCLEID}')
->setSingleId('{CIRCLEID}')
->setStatus(Member::STATUS_MEMBER)
->setLevel(Member::LEVEL_OWNER)
->setBasedOn($compareToOwnerBasedOn);
$compareTo = new Circle();
$compareTo->setOwner($compareToOwner)
->setConfig(Circle::CFG_SINGLE)
->setName('user:' . $userId . ':{CIRCLEID}')
->setDisplayName($userId);
$this->confirmCircleData($circle, $compareTo);
$this->r(true, $circle->getSingleId());
}
$this->p('Checking Single Circle for <comment>Circles App</comment>');
$circle = $this->getSingleCircleForMember($membersList, 'circles', $instance);
$compareToOwnerBasedOn = new Circle();
$compareToOwnerBasedOn->setConfig(Circle::CFG_SINGLE | Circle::CFG_ROOT)
->setName('app:circles:{CIRCLEID}')
->setDisplayName('circles');
$compareToOwner = new Member();
$compareToOwner->setUserId(Application::APP_ID)
->setUserType(Member::TYPE_APP)
->setInstance($instance)
->setDisplayName(Application::APP_ID)
->setId('{CIRCLEID}')
->setCircleId('{CIRCLEID}')
->setSingleId('{CIRCLEID}')
->setStatus(Member::STATUS_MEMBER)
->setLevel(Member::LEVEL_OWNER)
->setBasedOn($compareToOwnerBasedOn);
$compareTo = new Circle();
$compareTo->setOwner($compareToOwner)
->setConfig(Circle::CFG_SINGLE | Circle::CFG_ROOT)
->setName('app:circles:{CIRCLEID}')
->setDisplayName('circles');
$this->confirmCircleData($circle, $compareTo);
$this->r(true, $circle->getSingleId());
foreach ($this->getConfigArray($instanceId, 'groups') as $groupId => $members) {
$this->p('Checking Circle for <comment>' . $groupId . '@' . $instance . '</comment>');
$circle = $this->getCircleFromList($groupsList, 'group:' . $groupId);
$appCircle = $this->getSingleCircleForMember($membersList, 'circles', $instance);
$appOwner = $appCircle->getOwner();
$compareToOwnerBasedOn = new Circle();
$compareToOwnerBasedOn->setConfig(Circle::CFG_SINGLE | Circle::CFG_ROOT)
->setName($appCircle->getName())
->setDisplayName($appCircle->getDisplayName());
$compareToOwner = new Member();
$compareToOwner->setUserId($appOwner->getUserId())
->setUserType($appOwner->getUserType())
->setInstance($appOwner->getInstance())
->setDisplayName($appOwner->getDisplayName())
->setCircleId('{CIRCLEID}')
->setSingleId($appOwner->getSingleId())
->setStatus($appOwner->getStatus())
->setLevel($appOwner->getLevel())
->setBasedOn($compareToOwnerBasedOn);
$compareTo = new Circle();
$compareTo->setOwner($compareToOwner)
->setConfig(Circle::CFG_SYSTEM | Circle::CFG_NO_OWNER | Circle::CFG_HIDDEN)
->setName('group:' . $groupId)
->setDisplayName($groupId);
$this->confirmCircleData($circle, $compareTo);
$this->r(true, $circle->getSingleId());
}
$this->output->writeln('');
}
}
/**
*
*/
private function createRemoteLink() {
foreach ($this->getInstances() as $instanceId) {
$this->p('Init remote link from ' . $instanceId);
$links = $this->getConfigArray($instanceId, 'remote');
foreach ($links as $link => $type) {
$remote = $this->getConfig($link, 'config.frontal_cloud_id');
$this->pm($remote . '(' . $type . ')');
$this->occ($instanceId, 'circles:remote ' . $remote . ' --type ' . $type . ' --yes');
}
$this->r();
}
}
/**
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function buildingLocalDatabase() {
$this->circles = $this->federatedUsers = [];
foreach ($this->getInstances() as $instanceId) {
$this->p('Retrieving Circles from ' . $instanceId);
$circles = $this->occ($instanceId, 'circles:manage:list --all');
foreach ($circles as $item) {
/** @var Circle $circle */
$circle = $this->deserialize($item, Circle::class);
if ($circle->isConfig(Circle::CFG_SINGLE)) {
$pos = strrpos($circle->getName(), ':');
if (!$pos) {
throw new InvalidItemException('could not parse circle.name');
}
$owner = new FederatedUser();
$owner->importFromIFederatedUser($circle->getOwner());
$this->federatedUsers[$instanceId][$owner->getUserId()] = $owner;
$this->circles[$instanceId][substr($circle->getName(), 0, $pos)] = $circle;
} else {
$this->circles[$instanceId][$circle->getName()] = $circle;
}
}
$this->r();
}
}
/**
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function circleCreation001() {
$this->p('Creating basic circle');
$localInstanceId = 'global-scale-1';
$name = self::$TEST_CIRCLES[0];
$owner = $this->getInstanceUsers($localInstanceId)[1];
$dataCreatedCircle001 =
$this->occ($localInstanceId, 'circles:manage:create --type user ' . $owner . ' ' . $name);
/** @var Circle $createdCircle */
$createdCircle = $this->deserialize($dataCreatedCircle001, Circle::class);
$this->circles[$localInstanceId][$createdCircle->getName()] = $createdCircle;
$this->r(true, $createdCircle->getSingleId());;
$this->p('Comparing data returned at creation');
if ($createdCircle->getSingleId() === '' || $createdCircle->getOwner()->getId() === '') {
throw new InvalidItemException('empty id or owner.member_id');
}
$knownOwner = new Member();
$knownOwner->importFromIFederatedUser($this->federatedUsers[$localInstanceId][$owner]);
$knownOwner->setCircleId($createdCircle->getSingleId());
$knownOwner->setLevel(Member::LEVEL_OWNER);
$knownOwner->setStatus(Member::STATUS_MEMBER);
$knownOwner->setId($createdCircle->getOwner()->getId());
$compareTo = new Circle();
$compareTo->setOwner($knownOwner)
->setSingleId($createdCircle->getSingleId())
->setInitiator($knownOwner)
->setConfig(Circle::CFG_CIRCLE)
->setName($name)
->setDisplayName($name);
echo json_encode($createdCircle, JSON_PRETTY_PRINT);
$this->confirmCircleData($createdCircle, $compareTo, 'circle', true);
$this->r();
$this->p('Comparing local stored data');
$dataCircle = $this->occ($localInstanceId, 'circle:manage:details ' . $createdCircle->getSingleId());
/** @var Circle $tmpCircle */
$tmpCircle = $this->deserialize($dataCircle, Circle::class);
$this->confirmCircleData($tmpCircle, $createdCircle);
$this->r(true, $tmpCircle->getSingleId());
$links = $this->getConfigArray('global-scale-1', 'remote');
foreach ($this->getInstances(false) as $instanceId) {
$this->p('Comparing data stored on ' . $instanceId);
$dataCircle =
$this->occ($instanceId, 'circle:manage:details ' . $createdCircle->getSingleId(), false);
if ($instanceId === $localInstanceId || $links[$instanceId] === 'GlobalScale') {
/** @var Circle $tmpCircle */
$tmpCircle = $this->deserialize($dataCircle, Circle::class);
// we reset some data that should not be available on remote instance
$createdCircle->setInitiator(null)
->getOwner()->setBasedOn(null);
$this->confirmCircleData($tmpCircle, $createdCircle);
$this->r(true, $tmpCircle->getSingleId());
} else {
if (is_null($dataCircle)) {
$this->r(true, 'empty');
} else {
$this->r(false, 'should be empty');
}
}
}
}
/**
* @throws InvalidItemException
* @throws ItemNotFoundException
* @throws CircleNotFoundException
*/
private function addLocalMemberByUserId() {
$this->p('Adding local user as member, based on userId');
$instanceId = 'global-scale-1';
$circleName = self::$TEST_CIRCLES[0];
$member = $this->getInstanceUsers($instanceId)[2];
$addedMember = $this->processMemberAdd($instanceId, $circleName, $member, 'user');
$this->r(true, $addedMember->getId());;
// check test2
}
/**
* @throws CircleNotFoundException
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function addLocalMemberBySingleId() {
$this->p('Adding local user as member, based on singleId');
$localInstanceId = 'global-scale-1';
$name = self::$TEST_CIRCLES[0];
$circle = $this->getCircleByName($localInstanceId, $name);
$userId = $this->getInstanceUsers($localInstanceId)[6];
$userCircle = $this->getCircleByName($localInstanceId, 'user:' . $userId);
$user = $userCircle->getOwner();
$dataAddedMember =
$this->occ(
$localInstanceId, 'circles:members:add ' . $circle->getSingleId() . ' ' . $user->getSingleId()
);
/** @var Member $addedMember */
$addedMember = $this->deserialize($dataAddedMember, Member::class);
$this->r(true, $addedMember->getId());;
// check test6
}
private function addLocalMemberUsingMember() {
$this->p('Adding local member using local Member');
$localInstanceId = 'global-scale-1';
$initiator = $this->getInstanceUsers($localInstanceId)[6];
$member = $this->getInstanceUsers($localInstanceId)[6];
$circleName = self::$TEST_CIRCLES[0];
$circle = $this->getCircleByName($localInstanceId, $circleName);
$userId = $this->getInstanceUsers($localInstanceId)[6];
$userCircle = $this->getCircleByName($localInstanceId, 'user:' . $userId);
$user = $userCircle->getOwner();
$dataAddedMember =
$this->occ(
$localInstanceId, 'circles:members:add ' . $circle->getSingleId() . ' ' . $user->getSingleId()
);
/** @var Member $addedMember */
$addedMember = $this->deserialize($dataAddedMember, Member::class);
$this->r(true, $addedMember->getId());;
}
private function levelLocalMemberToModerator() {
$this->p('Changing local level to Moderator');
}
private function addLocalMemberUsingModerator() {
$this->p('Adding local member using local Moderator');
}
private function levelLocalMemberToModeratorUsingModerator() {
// fail
$this->p('Changing local level to moderator using local Moderator');
}
private function addRemoteMemberUsingModerator() {
$this->p('Adding remote user using local Moderator');
}
private function addLocalMemberUsingRemoteMember() {
// fail
$this->p('Adding local user using remote Member');
}
private function addRemoteMemberUsingRemoteMember() {
// fail
$this->p('Adding remote user using remote Member');
}
private function levelRemoteMemberToAdmin() {
$this->p('Changing remote level to Admin');
}
private function addLocalMemberUsingRemoteAdmin() {
$this->p('Adding remote member using remote Admin');
}
private function addRemoteMemberUsingRemoteAdmin() {
$this->p('Adding remote member using remote Admin');
}
private function levelRemoteMemberToModeratorUsingRemoteAdmin() {
}
private function levelRemoteMemberToAdminUsingRemoteAdmin() {
// fail
}
private function verifyMemberList001() {
}
private function removeLocalMemberUsingRemoteMember() {
// fail
}
private function removeLocalMemberUsingRemoteAdmin() {
}
private function removeRemoteMemberUsingRemoteMember() {
// fail
}
private function removeRemoteMemberUsingRemoteAdmin() {
}
/**
* @param Circle $circle
* @param Circle $compareTo
* @param string $prefix
* @param bool $versa
* @param array $params
*
* @throws Exception
*/
private function confirmCircleData(
Circle $circle,
Circle $compareTo,
string $prefix = 'circle',
bool $versa = false,
array $params = []
) {
if (empty($params)) {
$params = [
'CIRCLEID' => $circle->getSingleId()
];
}
$this->compare($compareTo->getSingleId(), $circle->getSingleId(), $prefix . '.id', $params);
$this->compare($compareTo->getName(), $circle->getName(), $prefix . '.name', $params);
$this->compare(
$compareTo->getDisplayName(), $circle->getDisplayName(), $prefix . '.displayName', $params
);
$this->compareInt($compareTo->getConfig(), $circle->getConfig(), $prefix . '.config', true);
$this->compareInt($compareTo->getSource(), $circle->getSource(), $prefix . '.source');
if ($compareTo->hasOwner()) {
$compareToOwner = $compareTo->getOwner();
if ($compareToOwner !== null) {
$owner = $circle->getOwner();
if ($owner === null) {
throw new Exception('empty owner');
}
if ($owner->getCircleId() !== $circle->getSingleId()) {
throw new Exception($prefix . '.owner.circleId is different than ' . $prefix . '.id');
}
$this->confirmMemberData($owner, $compareToOwner, 'owner', false, $params);
}
}
if ($compareTo->hasInitiator()) {
$compareToInitiator = $compareTo->getInitiator();
if ($compareToInitiator !== null) {
if (!$circle->hasInitiator()) {
throw new Exception('empty initiator');
}
$initiator = $circle->getInitiator();
if ($initiator->getCircleId() !== $circle->getSingleId()) {
throw new Exception($prefix . '.initiator.circleId is different than ' . $prefix . '.id');
}
$this->confirmMemberData($initiator, $compareToInitiator, 'owner', false, $params);
}
}
if ($versa) {
$this->confirmCircleData($compareTo, $circle);
}
}
/**
* @param Member $member
* @param Member $compareTo
* @param string $prefix
* @param bool $versa
* @param array $params
*
* @throws Exception
*/
private function confirmMemberData(
Member $member,
Member $compareTo,
string $prefix = 'member',
bool $versa = false,
array $params = []
) {
$this->compare($compareTo->getId(), $member->getId(), $prefix . '.id', $params);
$this->compare($compareTo->getCircleId(), $member->getCircleId(), $prefix . '.circleId', $params);
$this->compare($compareTo->getSingleId(), $member->getSingleId(), $prefix . '.singleId', $params);
$this->compare($compareTo->getUserId(), $member->getUserId(), $prefix . '.userId', $params);
$this->compare(
$compareTo->getDisplayName(), $member->getDisplayName(), $prefix . '.displayName', $params
);
$this->compareInt($compareTo->getUserType(), $member->getUserType(), $prefix . '.userType');
$this->compare($compareTo->getInstance(), $member->getInstance(), $prefix . '.instance', $params);
$this->compareInt($compareTo->getLevel(), $member->getLevel(), $prefix . '.level', true);
$this->compare($compareTo->getStatus(), $member->getStatus(), $prefix . '.status', $params);
$compareToBasedOn = $compareTo->getBasedOn();
if ($compareToBasedOn !== null) {
$basedOn = $member->getBasedOn();
if ($basedOn === null) {
throw new Exception('empty ' . $prefix . '.basedOn');
}
$this->confirmCircleData($basedOn, $compareToBasedOn, $prefix . '.basedOn', false, $params);
}
}
/**
* @param string $expected
* @param string $compare
* @param string $def
* @param array $params
*
* @throws Exception
*/
private function compare(string $expected, string $compare, string $def, array $params) {
if ($expected !== ''
&& $this->feedStringWithParams($expected, $params) !== $compare) {
throw new Exception($def . ': ' . $compare . ' (' . $expected . ')');
}
}
/**
* @param int $expected
* @param int $compare
* @param string $def
* @param array $params
* @param bool $force
*
* @throws Exception
*/
private function compareInt(int $expected, int $compare, string $def, bool $force = false) {
if (($expected > 0 || ($force && $expected >= 0))
&& $expected !== $compare) {
throw new Exception('wrong ' . $def . ': ' . $compare . ' (' . $expected . ')');
}
}
/**
* @param bool $localIncluded
*
* @return array
*/
private function getInstances(bool $localIncluded = true): array {
$instances = self::$INSTANCES;
if (!$localIncluded) {
$instances = array_diff($instances, [$this->local]);
}
return $instances;
}
/**
* @param array $circles
* @param string $userId
* @param string $instance
*
* @return Circle
* @throws CircleNotFoundException
*/
private function getSingleCircleForMember(array $circles, string $userId, string $instance): Circle {
foreach ($circles as $circle) {
$owner = $circle->getOwner();
if ($owner->getUserId() === $userId && $owner->getInstance() === $instance) {
return $circle;
}
}
throw new CircleNotFoundException('cannot find ' . $userId . ' in the list of Single Circle');
}
/**
* @param string $instanceId
* @param string $name
*
* @return Circle
* @throws CircleNotFoundException
*/
private function getCircleByName(string $instanceId, string $name): Circle {
if (array_key_exists($instanceId, $this->circles)
&& array_key_exists($name, $this->circles[$instanceId])) {
return $this->circles[$instanceId][$name];
}
throw new CircleNotFoundException(
'cannot extract \'' . $name . '\' from the list of generated Circles'
);
}
/**
* @param array $circles
* @param string $name
*
* @return Circle
* @throws CircleNotFoundException
*/
private function getCircleFromList(array $circles, string $name): Circle {
foreach ($circles as $circle) {
if ($circle->getName() === $name) {
return $circle;
}
}
throw new CircleNotFoundException(
'cannot extract \'' . $name . '\' from the list of provided Circles'
);
}
/**
* @param string $instance
* @param string $key
*
* @return string
* @throws ItemNotFoundException
*/
private function getConfig(string $instance, string $key): string {
$config = $this->getConfigInstance($instance);
return $this->get($key, $config);
}
/**
* @param string $instance
* @param string $key
*
* @return array
* @throws ItemNotFoundException
*/
private function getConfigArray(string $instance, string $key): array {
$config = $this->getConfigInstance($instance);
return $this->getArray($key, $config);
}
/**
* @param string $instance
*
* @return array
* @throws ItemNotFoundException
*/
private function getConfigInstance(string $instance): array {
foreach ($this->getArray('instances', $this->config) as $item) {
if (strtolower($this->get('id', $item)) === strtolower($instance)) {
return $item;
}
}
throw new ItemNotFoundException($instance . ' not found');
}
/**
* @param $instanceId
*
* @return array
* @throws ItemNotFoundException
*/
private function getInstanceUsers($instanceId): array {
return $this->getConfigArray($instanceId, 'users');
}
/**
* @param string $instance
* @param string $cmd
* @param bool $exceptionOnFail
* @param bool $jsonAsOutput
* @param array $env
*
* @return array
* @throws ItemNotFoundException
* @throws Exception
*/
private function occ(
string $instance,
string $cmd,
bool $exceptionOnFail = true,
bool $jsonAsOutput = true,
array $env = []
): ?array {
$configInstance = $this->getConfigInstance($instance);
$path = $this->get('path', $configInstance);
$occ = rtrim($path, '/') . '/occ';
$command = array_merge([$occ], explode(' ', $cmd));
if ($jsonAsOutput) {
$command = array_merge($command, ['--output=json']);
}
$process = new Process($command);
$process->run(null, $env);
if ($exceptionOnFail && !$process->isSuccessful()) {
throw new Exception(implode(' ', $command) . ' failed');
}
$output = json_decode($process->getOutput(), true);
if (!is_array($output)) {
return null;
}
return $output;
}
//
//
//
/**
* @param string $title
*/
private function t(string $title): void {
$this->output->writeln('');
$this->output->writeln('<comment>### ' . $title . '</comment>');
$this->output->writeln('');
}
/**
* @param string $processing
*/
private function p(string $processing): void {
$this->pOn = true;
$this->output->write('- ' . $processing . ': ');
}
/**
* @param string $more
*/
private function pm(string $more): void {
$this->output->write($more . ' ');
}
/**
* @param bool $result
* @param string $info
*/
private function r(bool $result = true, string $info = ''): void {
$this->pOn = false;
if ($result) {
$this->output->writeln('<info>' . (($info !== '') ? $info : 'done') . '</info>');
} else {
$this->output->writeln('<error>' . (($info !== '') ? $info : 'done') . '</error>');
}
}
/**
* @param string $instanceId
* @param string $circleName
* @param string $userId
* @param string $type
*
* @return Member
* @throws CircleNotFoundException
* @throws InvalidItemException
* @throws ItemNotFoundException
*/
private function processMemberAdd(string $instanceId, string $circleName, string $userId, string $type
): Member {
$circle = $this->getCircleByName($instanceId, $circleName);
$dataAddedMember =
$this->occ(
$instanceId,
'circles:members:add ' . $circle->getSingleId() . ' ' . $userId . ' --type ' . $type
);
/** @var Member $addedMember */
$addedMember = $this->deserialize($dataAddedMember, Member::class);
echo 'ADDEDMEMBER: ' . json_encode($addedMember, JSON_PRETTY_PRINT) . "\n";
$federatedUser = $this->federatedUsers[$instanceId][$userId];
echo 'FEDERATEDUER: ' . json_encode($federatedUser, JSON_PRETTY_PRINT) . "\n";
return $addedMember;
}
}