Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add profile config migration #33177

Merged
merged 1 commit into from
Sep 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions apps/settings/lib/UserMigration/AccountMigrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@

use InvalidArgumentException;
use OC\Accounts\TAccountsHelper;
use OC\Core\Db\ProfileConfigMapper;
use OC\NotSquareException;
use OC\Profile\ProfileManager;
use OCA\Settings\AppInfo\Application;
use OCP\Accounts\IAccountManager;
use OCP\IAvatarManager;
Expand All @@ -51,6 +53,10 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {

private IAvatarManager $avatarManager;

private ProfileManager $profileManager;

private ProfileConfigMapper $configMapper;

private IL10N $l10n;

private const PATH_ROOT = Application::APP_ID . '/';
Expand All @@ -59,13 +65,19 @@ class AccountMigrator implements IMigrator, ISizeEstimationMigrator {

private const AVATAR_BASENAME = 'avatar';

private const PATH_CONFIG_FILE = AccountMigrator::PATH_ROOT . 'config.json';

public function __construct(
IAccountManager $accountManager,
IAvatarManager $avatarManager,
ProfileManager $profileManager,
ProfileConfigMapper $configMapper,
IL10N $l10n
) {
$this->accountManager = $accountManager;
$this->avatarManager = $avatarManager;
$this->profileManager = $profileManager;
$this->configMapper = $configMapper;
$this->l10n = $l10n;
}

Expand Down Expand Up @@ -113,6 +125,14 @@ public function export(IUser $user, IExportDestination $exportDestination, Outpu
} catch (Throwable $e) {
throw new AccountMigratorException('Could not export avatar', 0, $e);
}

try {
$output->writeln('Exporting profile config in ' . AccountMigrator::PATH_CONFIG_FILE . '…');
$config = $this->profileManager->getProfileConfig($user, $user);
$exportDestination->addFileContents(AccountMigrator::PATH_CONFIG_FILE, json_encode($config));
} catch (Throwable $e) {
throw new AccountMigratorException('Could not export profile config', 0, $e);
}
}

/**
Expand Down Expand Up @@ -165,6 +185,19 @@ public function import(IUser $user, IImportSource $importSource, OutputInterface
throw new AccountMigratorException('Failed to import avatar', 0, $e);
}
}

try {
$output->writeln('Importing profile config from ' . AccountMigrator::PATH_CONFIG_FILE . '…');
/** @var array $configData */
$configData = json_decode($importSource->getFileContents(AccountMigrator::PATH_CONFIG_FILE), true, 512, JSON_THROW_ON_ERROR);
// Ensure that a profile config entry exists in the database
$this->profileManager->getProfileConfig($user, $user);
$config = $this->configMapper->get($user->getUID());
$config->setConfigArray($configData);
$this->configMapper->update($config);
} catch (Throwable $e) {
throw new AccountMigratorException('Failed to import profile config');
}
}

/**
Expand Down
43 changes: 29 additions & 14 deletions apps/settings/tests/UserMigration/AccountMigratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use OCP\IUserManager;
use OCP\UserMigration\IExportDestination;
use OCP\UserMigration\IImportSource;
use PHPUnit\Framework\Constraint\JsonMatches;
use PHPUnit\Framework\MockObject\MockObject;
use Sabre\VObject\UUIDUtil;
use Symfony\Component\Console\Output\OutputInterface;
Expand Down Expand Up @@ -65,6 +66,8 @@ class AccountMigratorTest extends TestCase {

private const REGEX_AVATAR_FILE = '/^' . Application::APP_ID . '\/' . 'avatar\.(jpg|png)' . '$/';

private const REGEX_CONFIG_FILE = '/^' . Application::APP_ID . '\/' . '[a-z]+\.json' . '$/';

protected function setUp(): void {
$app = new App(Application::APP_ID);
$container = $app->getContainer();
Expand All @@ -81,30 +84,33 @@ protected function setUp(): void {
public function dataImportExportAccount(): array {
return array_map(
function (string $filename) {
$dataPath = self::ASSETS_DIR . $filename;
// For each json file there is an avatar image with the same basename
$avatarBasename = pathinfo($filename, PATHINFO_FILENAME);
$avatarPath = self::ASSETS_DIR . (file_exists(self::ASSETS_DIR . "$avatarBasename.jpg") ? "$avatarBasename.jpg" : "$avatarBasename.png");
$dataPath = static::ASSETS_DIR . $filename;
// For each account json file there is an avatar image and a config json file with the same basename
$basename = pathinfo($filename, PATHINFO_FILENAME);
$avatarPath = static::ASSETS_DIR . (file_exists(static::ASSETS_DIR . "$basename.jpg") ? "$basename.jpg" : "$basename.png");
$configPath = static::ASSETS_DIR . "$basename-config." . pathinfo($filename, PATHINFO_EXTENSION);
return [
UUIDUtil::getUUID(),
json_decode(file_get_contents($dataPath), true, 512, JSON_THROW_ON_ERROR),
$avatarPath,
json_decode(file_get_contents($configPath), true, 512, JSON_THROW_ON_ERROR),
];
},
array_filter(
scandir(self::ASSETS_DIR),
fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json',
scandir(static::ASSETS_DIR),
fn (string $filename) => pathinfo($filename, PATHINFO_EXTENSION) === 'json' && mb_strpos(pathinfo($filename, PATHINFO_FILENAME), 'config') === false,
),
);
}

/**
* @dataProvider dataImportExportAccount
*/
public function testImportExportAccount(string $userId, array $importData, string $avatarPath): void {
public function testImportExportAccount(string $userId, array $importData, string $avatarPath, array $importConfig): void {
$user = $this->userManager->createUser($userId, 'topsecretpassword');
$avatarExt = pathinfo($avatarPath, PATHINFO_EXTENSION);
$exportData = $importData;
$exportConfig = $importConfig;
// Verification status of email will be set to in progress on import so we set the export data to reflect that
$exportData[IAccountManager::PROPERTY_EMAIL]['verified'] = IAccountManager::VERIFICATION_IN_PROGRESS;

Expand All @@ -115,10 +121,16 @@ public function testImportExportAccount(string $userId, array $importData, strin
->willReturn(1);

$this->importSource
->expects($this->once())
->expects($this->exactly(2))
->method('getFileContents')
->with($this->matchesRegularExpression(self::REGEX_ACCOUNT_FILE))
->willReturn(json_encode($importData));
->withConsecutive(
[$this->matchesRegularExpression(static::REGEX_ACCOUNT_FILE)],
[$this->matchesRegularExpression(static::REGEX_CONFIG_FILE)],
)
->willReturnOnConsecutiveCalls(
json_encode($importData),
json_encode($importConfig),
);

$this->importSource
->expects($this->once())
Expand All @@ -129,7 +141,7 @@ public function testImportExportAccount(string $userId, array $importData, strin
$this->importSource
->expects($this->once())
->method('getFileAsStream')
->with($this->matchesRegularExpression(self::REGEX_AVATAR_FILE))
->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE))
->willReturn(fopen($avatarPath, 'r'));

$this->migrator->import($user, $this->importSource, $this->output);
Expand All @@ -150,14 +162,17 @@ public function testImportExportAccount(string $userId, array $importData, strin
}

$this->exportDestination
->expects($this->once())
->expects($this->exactly(2))
->method('addFileContents')
->with($this->matchesRegularExpression(self::REGEX_ACCOUNT_FILE), json_encode($exportData));
->withConsecutive(
[$this->matchesRegularExpression(static::REGEX_ACCOUNT_FILE), new JsonMatches(json_encode($exportData))],
[$this->matchesRegularExpression(static::REGEX_CONFIG_FILE), new JsonMatches(json_encode($exportConfig))],
);

$this->exportDestination
->expects($this->once())
->method('addFileAsStream')
->with($this->matchesRegularExpression(self::REGEX_AVATAR_FILE), $this->isType('resource'));
->with($this->matchesRegularExpression(static::REGEX_AVATAR_FILE), $this->isType('resource'));

$this->migrator->export($user, $this->exportDestination, $this->output);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"address":{"visibility":"show_users_only"},"avatar":{"visibility":"show_users_only"},"biography":{"visibility":"show"},"displayname":{"visibility":"show"},"headline":{"visibility":"show"},"organisation":{"visibility":"show"},"role":{"visibility":"show"},"email":{"visibility":"hide"},"phone":{"visibility":"hide"},"twitter":{"visibility":"show_users_only"},"website":{"visibility":"show_users_only"},"talk":{"visibility":"show"}}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"address":{"visibility":"show_users_only"},"avatar":{"visibility":"show"},"biography":{"visibility":"show"},"displayname":{"visibility":"show"},"headline":{"visibility":"show"},"organisation":{"visibility":"show"},"role":{"visibility":"show"},"email":{"visibility":"show_users_only"},"phone":{"visibility":"show_users_only"},"twitter":{"visibility":"show"},"website":{"visibility":"show"}}