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

IBX-4906: Migrate richtext namespaces #67

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3ea8063
Implemented ibexa:migrate:richtext-namespaces - single process
vidarl Jan 27, 2023
d60ed00
Required symfony/process
vidarl Jan 27, 2023
e140fa2
Added support for concurrency in ibexa:migrate:richtext-namespaces
vidarl Jan 30, 2023
a023267
CS fix
vidarl Jan 30, 2023
dfe0363
fixup! Implemented ibexa:migrate:richtext-namespaces - single process
vidarl Jan 30, 2023
822d2e5
fixup! Added support for concurrency in ibexa:migrate:richtext-namesp…
vidarl Jan 30, 2023
292db12
Fixed erroneous composer.json
alongosz Jan 30, 2023
273d179
Update src/bundle/Command/MultiprocessComand.php
vidarl Jan 30, 2023
8264f2d
review fixes
vidarl Jan 30, 2023
af2da2c
Fixed PHP 7.4 compliance
vidarl Jan 30, 2023
5abb5f7
Added message about fetching objectCount may take some time
vidarl Feb 3, 2023
359c7b8
fixup! Added message about fetching objectCount may take some time
vidarl Feb 3, 2023
69ebb1d
fixup! Added message about fetching objectCount may take some time
vidarl Feb 3, 2023
41c2f92
Fixup ! Added run-time support for old ezno namepaces - tests
vidarl Feb 6, 2023
fd3f472
Added run-time support for old ezno namepaces
vidarl Feb 6, 2023
ef404f9
Added support for more old ez.no namespaces
vidarl Feb 13, 2023
d59f3de
Added tests for MigrateNamespacesCommand
vidarl Feb 13, 2023
0aa233f
fixup! Fixup ! Added run-time support for old ezno namepaces
vidarl Feb 13, 2023
59e7fe9
[Composer] Disabled all plugins by default
vidarl Feb 13, 2023
8263cb3
Apply suggestions from code review
vidarl Feb 17, 2023
bf273e4
fixup! Apply suggestions from code review
vidarl Feb 17, 2023
a38f00f
fixup! fixup! Apply suggestions from code review
vidarl Feb 17, 2023
08f79a6
Counting fieldattributes that was or was not changed by ibexa:migrate…
vidarl Feb 17, 2023
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
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"ibexa/content-forms": "~4.3.0@dev",
"ibexa/rest": "~4.3.0@dev",
"ibexa/http-cache": "~4.3.0@dev"
alongosz marked this conversation as resolved.
Show resolved Hide resolved
"symfony/process": "*"
vidarl marked this conversation as resolved.
Show resolved Hide resolved
},
"require-dev": {
"ibexa/ci-scripts": "^0.2@dev",
Expand Down
144 changes: 144 additions & 0 deletions src/bundle/Command/MigrateNamespacesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Bundle\FieldTypeRichText\Command;

use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\UserService;
use Ibexa\FieldTypeRichText\Persistence\Legacy\ContentModelGateway as Gateway;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

final class MigrateNamespacesCommand extends MultiprocessComand
{
private Gateway $gateway;

private ?int $cursorStart;

private ?int $cursorStop;

public function __construct(
PermissionResolver $permissionResolver,
UserService $userService,
Gateway $gateway,
) {
parent::__construct('ibexa:migrate:richtext-namespaces', $permissionResolver, $userService);
$this->gateway = $gateway;
}

public function configure(): void
{
parent::configure();

$this->addOption(
'cursor-start',
null,
InputOption::VALUE_REQUIRED,
'Internal option - only used for subprocesses',
)
->addOption(
'cursor-stop',
null,
InputOption::VALUE_REQUIRED,
'Internal option - only used for subprocesses',
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->cursorStart = $input->getOption('cursor-start') !== null ? (int) $input->getOption('cursor-start') : null;
$this->cursorStop = $input->getOption('cursor-stop') !== null ? (int) $input->getOption('cursor-stop') : null;

// Check that both --cursor-start and cursor-start are set, or neither
if (($this->cursorStart === null) xor ($this->cursorStop === null)) {
throw new RuntimeException('The options --cursor-start and -cursor-stop are only for internal use !');
}

parent::execute($input, $output);

return self::SUCCESS;
}

protected function getObjectCount(): int
{
return $this->gateway->countRichtextAttributes();
}

protected function iterate(): void
{
$limit = $this->getIterationCount();
$cursor = [
'start' => -1,
'stop' => null,
];

$contentAttributeIDs = $this->gateway->getContentObjectAttributeIds($cursor['start'], $limit);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since contentobject_attribute.id is not unique, I found no better way than to fetch every single id in the mother process as I wanted to avoid using offset in the SQLs later on when iterating over the dataset

$cursor['stop'] = $this->getNextCursor($contentAttributeIDs);
while ($cursor['stop'] !== null) {
$this->createChildProcess($cursor, count($contentAttributeIDs));

$cursor['start'] = $cursor['stop'];
$contentAttributeIDs = $this->gateway->getContentObjectAttributeIds($cursor['start'], $limit);
$cursor['stop'] = $this->getNextCursor($contentAttributeIDs);
}
}

protected function completed(): void
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: method name should be a verb in present simple, unless we have a need for past participle, like isCompleted. This case is neither of those, so it should be complete, finalize, or finish.

{
$this->output->writeln(PHP_EOL . 'Completed');
}

protected function getNextCursor(array $contentAttributeIDs): ?int
{
$lastId = count($contentAttributeIDs) > 0 ? end($contentAttributeIDs)['id'] : null;

return $lastId;
Comment on lines +120 to +122
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to allocate extra memory for $lastId. You should return it immediately:

Suggested change
$lastId = count($contentAttributeIDs) > 0 ? end($contentAttributeIDs)['id'] : null;
return $lastId;
return count($contentAttributeIDs) > 0 ? end($contentAttributeIDs)['id'] : null;

}
Comment on lines +118 to +123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me it crashes

Return value of Ibexa\Bundle\FieldTypeRichText\Command\MigrateNamespacesCommand::getNextCursor() must be of the type int or null, string returned

Hint: I'm using MySQL. It's a responsibility of a Gateway to return data consisting of proper types. array_map('intval', ...) can be used to make uniform dbms-independent code.


protected function processData($cursor)
{
$this->updateNamespacesInColumns($cursor['start'], $cursor['stop']);
}

protected function constructCursorFromInputOptions(): mixed
{
return [
'start' => $this->cursorStart,
'stop' => $this->cursorStop,
];
}

protected function addChildProcessArguments($cursor): array
{
return [
'--cursor-start=' . $cursor['start'],
'--cursor-stop=' . $cursor['stop'],
];
}

protected function isChildProcess(): bool
{
return $this->cursorStart !== null || $this->cursorStop !== null;
}

protected function updateNamespacesInColumns(int $contentAttributeIdStart, int $contentAttributeIdStop): void
{
$contentAttributes = $this->gateway->getContentObjectAttributes($contentAttributeIdStart, $contentAttributeIdStop);

foreach ($contentAttributes as $contentAttribute) {
$contentAttribute['data_text'] = str_replace('xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml"', 'xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml"', $contentAttribute['data_text']);
$contentAttribute['data_text'] = str_replace('xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom"', 'xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom"', $contentAttribute['data_text']);
Copy link
Contributor Author

@vidarl vidarl Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change ezxhtml and ezcustom to ibexaxhtml and ibexacustom while we are at it?

That would maybe require some changes in editor and other places too though ?


if (!$this->isDryRun()) {
$this->gateway->updateContentObjectAttribute($contentAttribute['data_text'], $contentAttribute['contentobject_id'], $contentAttribute['id'], $contentAttribute['version'], $contentAttribute['language_code']);
}
}
}
}
Loading