Merge pull request #46009 from nextcloud/refactor/ajax-cron

refactor(cron): Use `IAppConfig` for cron settings and migrate ajax cron away from jQuery
This commit is contained in:
Ferdinand Thiessen 2024-06-29 16:29:34 +02:00 committed by GitHub
commit 682faad0b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 232 additions and 92 deletions

View file

@ -41,7 +41,7 @@ class Server implements IDelegatedSettings {
$cliBasedCronUser = $cliBasedCronPossible ? (posix_getpwuid($ownerConfigFile)['name'] ?? '') : '';
// Background jobs
$this->initialStateService->provideInitialState('backgroundJobsMode', $this->config->getAppValue('core', 'backgroundjobs_mode', 'ajax'));
$this->initialStateService->provideInitialState('backgroundJobsMode', $this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax'));
$this->initialStateService->provideInitialState('lastCron', $this->appConfig->getValueInt('core', 'lastcron', 0));
$this->initialStateService->provideInitialState('cronMaxAge', $this->cronMaxAge());
$this->initialStateService->provideInitialState('cronErrors', $this->config->getAppValue('core', 'cronErrors'));

View file

@ -25,23 +25,23 @@ use Test\TestCase;
* @group DB
*/
class ServerTest extends TestCase {
/** @var Server */
private $admin;
/** @var IDBConnection */
private $connection;
/** @var IInitialState */
/** @var Server&MockObject */
private $admin;
/** @var IInitialState&MockObject */
private $initialStateService;
/** @var ProfileManager */
/** @var ProfileManager&MockObject */
private $profileManager;
/** @var ITimeFactory|MockObject */
/** @var ITimeFactory&MockObject */
private $timeFactory;
/** @var IConfig|MockObject */
/** @var IConfig&MockObject */
private $config;
/** @var IAppConfig|MockObject */
/** @var IAppConfig&MockObject */
private $appConfig;
/** @var IL10N|MockObject */
/** @var IL10N&MockObject */
private $l10n;
/** @var IUrlGenerator|MockObject */
/** @var IUrlGenerator&MockObject */
private $urlGenerator;
protected function setUp(): void {
@ -78,10 +78,14 @@ class ServerTest extends TestCase {
->expects($this->any())
->method('getAppValue')
->willReturnMap([
['core', 'backgroundjobs_mode', 'ajax', 'ajax'],
['core', 'lastcron', '0', '0'],
['core', 'cronErrors', ''],
]);
$this->appConfig
->expects($this->any())
->method('getValueString')
->with('core', 'backgroundjobs_mode', 'ajax')
->willReturn('ajax');
$this->profileManager
->expects($this->exactly(2))
->method('isProfileEnabled')

View file

@ -16,6 +16,7 @@ use OCA\User_LDAP\Mapping\AbstractMapping;
use OCA\User_LDAP\User\Manager;
use OCA\User_LDAP\User\OfflineUser;
use OCP\HintException;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
@ -30,10 +31,6 @@ use function substr;
class Access extends LDAPUtility {
public const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
/** @var \OCA\User_LDAP\Connection */
public $connection;
/** @var Manager */
public $userManager;
/**
* never ever check this var directly, always use getPagedSearchResultState
* @var ?bool
@ -45,27 +42,17 @@ class Access extends LDAPUtility {
/** @var ?AbstractMapping */
protected $groupMapper;
/**
* @var \OCA\User_LDAP\Helper
*/
private $helper;
/** @var IConfig */
private $config;
/** @var IUserManager */
private $ncUserManager;
/** @var LoggerInterface */
private $logger;
private string $lastCookie = '';
public function __construct(
Connection $connection,
ILDAPWrapper $ldap,
Manager $userManager,
Helper $helper,
IConfig $config,
IUserManager $ncUserManager,
LoggerInterface $logger
public Connection $connection,
public Manager $userManager,
private Helper $helper,
private IConfig $config,
private IUserManager $ncUserManager,
private LoggerInterface $logger,
private IAppConfig $appConfig,
) {
parent::__construct($ldap);
$this->connection = $connection;
@ -822,8 +809,7 @@ class Access extends LDAPUtility {
$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
$recordsToUpdate = $ldapRecords;
if (!$forceApplyAttributes) {
$isBackgroundJobModeAjax = $this->config
->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
$isBackgroundJobModeAjax = $this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
$listOfDNs = array_reduce($ldapRecords, function ($listOfDNs, $entry) {
$listOfDNs[] = $entry['dn'][0];
return $listOfDNs;

View file

@ -6,24 +6,22 @@
namespace OCA\User_LDAP;
use OCA\User_LDAP\User\Manager;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\Server;
use Psr\Log\LoggerInterface;
class AccessFactory {
private ILDAPWrapper $ldap;
private Helper $helper;
private IConfig $config;
private IUserManager $ncUserManager;
private LoggerInterface $logger;
public function __construct(
ILDAPWrapper $ldap,
Helper $helper,
IConfig $config,
IUserManager $ncUserManager,
LoggerInterface $logger) {
private ILDAPWrapper $ldap,
private Helper $helper,
private IConfig $config,
private IAppConfig $appConfig,
private IUserManager $ncUserManager,
private LoggerInterface $logger,
) {
$this->ldap = $ldap;
$this->helper = $helper;
$this->config = $config;
@ -34,13 +32,14 @@ class AccessFactory {
public function get(Connection $connection): Access {
/* Each Access instance gets its own Manager instance, see OCA\User_LDAP\AppInfo\Application::register() */
return new Access(
$connection,
$this->ldap,
$connection,
Server::get(Manager::class),
$this->helper,
$this->config,
$this->ncUserManager,
$this->logger
$this->logger,
$this->appConfig,
);
}
}

View file

@ -18,12 +18,14 @@ use OCA\User_LDAP\Mapping\UserMapping;
use OCA\User_LDAP\User\Manager;
use OCA\User_LDAP\User\OfflineUser;
use OCA\User_LDAP\User\User;
use OCP\IAppConfig;
use OCP\IAvatarManager;
use OCP\IConfig;
use OCP\Image;
use OCP\IUserManager;
use OCP\Notification\IManager as INotificationManager;
use OCP\Share\IManager;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
@ -53,10 +55,12 @@ class AccessTest extends TestCase {
private $config;
/** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
private $ncUserManager;
/** @var LoggerInterface|MockObject */
private $logger;
/** @var Access */
private $access;
private LoggerInterface&MockObject $logger;
private IAppConfig&MockObject $appConfig;
private Access $access;
protected function setUp(): void {
$this->connection = $this->createMock(Connection::class);
@ -69,28 +73,33 @@ class AccessTest extends TestCase {
$this->ncUserManager = $this->createMock(IUserManager::class);
$this->shareManager = $this->createMock(IManager::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->access = new Access(
$this->connection,
$this->ldap,
$this->connection,
$this->userManager,
$this->helper,
$this->config,
$this->ncUserManager,
$this->logger
$this->logger,
$this->appConfig,
);
$this->access->setUserMapper($this->userMapper);
$this->access->setGroupMapper($this->groupMapper);
}
private function getConnectorAndLdapMock() {
/** @var ILDAPWrapper&MockObject */
$lw = $this->createMock(ILDAPWrapper::class);
/** @var Connection&MockObject */
$connector = $this->getMockBuilder(Connection::class)
->setConstructorArgs([$lw, '', null])
->getMock();
$connector->expects($this->any())
->method('getConnectionResource')
->willReturn(ldap_connect('ldap://example.com'));
/** @var Manager&MockObject */
$um = $this->getMockBuilder(Manager::class)
->setConstructorArgs([
$this->createMock(IConfig::class),
@ -220,7 +229,7 @@ class AccessTest extends TestCase {
[$lw, $con, $um, $helper] = $this->getConnectorAndLdapMock();
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */
$config = $this->createMock(IConfig::class);
$access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager, $this->logger);
$access = new Access($lw, $con, $um, $helper, $config, $this->ncUserManager, $this->logger, $this->appConfig);
$lw->expects($this->exactly(1))
->method('explodeDN')
@ -243,7 +252,7 @@ class AccessTest extends TestCase {
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject $config */
$config = $this->createMock(IConfig::class);
$lw = new LDAP();
$access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager, $this->logger);
$access = new Access($lw, $con, $um, $helper, $config, $this->ncUserManager, $this->logger, $this->appConfig);
if (!function_exists('ldap_explode_dn')) {
$this->markTestSkipped('LDAP Module not available');
@ -429,7 +438,7 @@ class AccessTest extends TestCase {
$attribute => ['count' => 1, $dnFromServer]
]);
$access = new Access($con, $lw, $um, $helper, $config, $this->ncUserManager, $this->logger);
$access = new Access($lw, $con, $um, $helper, $config, $this->ncUserManager, $this->logger, $this->appConfig);
$values = $access->readAttribute('uid=whoever,dc=example,dc=org', $attribute);
$this->assertSame($values[0], strtolower($dnFromServer));
}

View file

@ -5,7 +5,7 @@
*/
namespace OC\Core\Command\Background;
use OCP\IConfig;
use OCP\IAppConfig;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@ -17,18 +17,17 @@ use Symfony\Component\Console\Output\OutputInterface;
* Subclasses will override the getMode() function to specify the mode to configure.
*/
abstract class Base extends Command {
abstract protected function getMode();
abstract protected function getMode(): string;
public function __construct(
protected IConfig $config,
protected IAppConfig $config,
) {
parent::__construct();
}
protected function configure(): void {
$mode = $this->getMode();
$this
->setName("background:$mode")
$this->setName("background:$mode")
->setDescription("Use $mode to run background jobs");
}
@ -43,7 +42,7 @@ abstract class Base extends Command {
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$mode = $this->getMode();
$this->config->setAppValue('core', 'backgroundjobs_mode', $mode);
$this->config->setValueString('core', 'backgroundjobs_mode', $mode);
$output->writeln("Set mode for background jobs to '$mode'");
return 0;
}

View file

@ -13,12 +13,14 @@ use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IConfig;
use OCP\IAppConfig;
use OCP\Util;
/** @template-implements IEventListener<BeforeLoginTemplateRenderedEvent|BeforeTemplateRenderedEvent> */
class BeforeTemplateRenderedListener implements IEventListener {
public function __construct(private IConfig $config) {
public function __construct(
private IAppConfig $appConfig,
) {
}
public function handle(Event $event): void {
@ -53,8 +55,8 @@ class BeforeTemplateRenderedListener implements IEventListener {
// If installed and background job is set to ajax, add dedicated script
if ($this->config->getAppValue('core', 'backgroundjobs_mode', 'ajax') == 'ajax') {
Util::addScript('core', 'backgroundjobs');
if ($this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax') === 'ajax') {
Util::addScript('core', 'ajax-cron');
}
}
}

View file

@ -1,13 +0,0 @@
/**
* SPDX-FileCopyrightText: 2012 Jakob Sack owncloud@jakobsack.de
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
// start worker once page has loaded
window.addEventListener('DOMContentLoaded', function(){
$.get( OC.getRootPath()+'/cron.php' );
$('.section .icon-info').tooltip({
placement: 'right'
});
});

View file

@ -14,7 +14,6 @@ window.addEventListener('DOMContentLoaded', function () {
$('#remote_address').focus();
});
$(document).mouseup(function(e) {
var toggle = $('#body-public').find('.header-right .menutoggle');
var container = toggle.next('.popovermenu');

18
core/src/ajax-cron.ts Normal file
View file

@ -0,0 +1,18 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getRootUrl } from '@nextcloud/router'
import logger from './logger'
window.addEventListener('DOMContentLoaded', async () => {
// When the page is loaded send GET to the cron endpoint to trigger background jobs
try {
logger.debug('Running web cron')
await window.fetch(`${getRootUrl()}/cron.php`)
logger.debug('Web cron successfull')
} catch (e) {
logger.debug('Running web cron failed', { error: e })
}
})

2
dist/core-ajax-cron.js vendored Normal file
View file

@ -0,0 +1,2 @@
(()=>{"use strict";var e,r={78038:(e,r,n)=>{var o=n(63814),t=n(21777),i=n(35947);const d=null===(a=(0,t.HW)())?(0,i.YK)().setApp("core").build():(0,i.YK)().setApp("core").setUid(a.uid).build();var a;(0,i.YK)().setApp("unified-search").detectUser().build(),window.addEventListener("DOMContentLoaded",(async()=>{try{d.debug("Running web cron"),await window.fetch("".concat((0,o.aU)(),"/cron.php")),d.debug("Web cron successfull")}catch(e){d.debug("Running web cron failed",{error:e})}}))}},n={};function o(e){var t=n[e];if(void 0!==t)return t.exports;var i=n[e]={id:e,loaded:!1,exports:{}};return r[e].call(i.exports,i,i.exports,o),i.loaded=!0,i.exports}o.m=r,e=[],o.O=(r,n,t,i)=>{if(!n){var d=1/0;for(u=0;u<e.length;u++){n=e[u][0],t=e[u][1],i=e[u][2];for(var a=!0,l=0;l<n.length;l++)(!1&i||d>=i)&&Object.keys(o.O).every((e=>o.O[e](n[l])))?n.splice(l--,1):(a=!1,i<d&&(d=i));if(a){e.splice(u--,1);var c=t();void 0!==c&&(r=c)}}return r}i=i||0;for(var u=e.length;u>0&&e[u-1][2]>i;u--)e[u]=e[u-1];e[u]=[n,t,i]},o.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return o.d(r,{a:r}),r},o.d=(e,r)=>{for(var n in r)o.o(r,n)&&!o.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:r[n]})},o.e=()=>Promise.resolve(),o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),o.j=5438,(()=>{o.b=document.baseURI||self.location.href;var e={5438:0};o.O.j=r=>0===e[r];var r=(r,n)=>{var t,i,d=n[0],a=n[1],l=n[2],c=0;if(d.some((r=>0!==e[r]))){for(t in a)o.o(a,t)&&(o.m[t]=a[t]);if(l)var u=l(o)}for(r&&r(n);c<d.length;c++)i=d[c],o.o(e,i)&&e[i]&&e[i][0](),e[i]=0;return o.O(u)},n=self.webpackChunknextcloud=self.webpackChunknextcloud||[];n.forEach(r.bind(null,0)),n.push=r.bind(null,n.push.bind(n))})(),o.nc=void 0;var t=o.O(void 0,[4208],(()=>o(78038)));t=o.O(t)})();
//# sourceMappingURL=core-ajax-cron.js.map?v=c8767a73e251711105c9

132
dist/core-ajax-cron.js.license vendored Normal file
View file

@ -0,0 +1,132 @@
SPDX-License-Identifier: MIT
SPDX-License-Identifier: ISC
SPDX-License-Identifier: GPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-FileCopyrightText: inherits developers
SPDX-FileCopyrightText: assert developers
SPDX-FileCopyrightText: Tobias Koppers @sokra
SPDX-FileCopyrightText: Roman Shtylman <shtylman@gmail.com>
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
SPDX-FileCopyrightText: Joyent
SPDX-FileCopyrightText: Jordan Harband <ljharb@gmail.com>
SPDX-FileCopyrightText: Jordan Harband
SPDX-FileCopyrightText: GitHub Inc.
SPDX-FileCopyrightText: Christoph Wurst
This file is generated from multiple sources. Included packages:
- @nextcloud/auth
- version: 2.3.0
- license: GPL-3.0-or-later
- semver
- version: 7.6.2
- license: ISC
- @nextcloud/event-bus
- version: 3.3.1
- license: GPL-3.0-or-later
- @nextcloud/logger
- version: 3.0.2
- license: GPL-3.0-or-later
- @nextcloud/router
- version: 3.0.1
- license: GPL-3.0-or-later
- assert
- version: 2.1.0
- license: MIT
- available-typed-arrays
- version: 1.0.7
- license: MIT
- call-bind
- version: 1.0.7
- license: MIT
- console-browserify
- version: 1.2.0
- license: MIT
- define-data-property
- version: 1.1.4
- license: MIT
- define-properties
- version: 1.2.1
- license: MIT
- es-define-property
- version: 1.0.0
- license: MIT
- es-errors
- version: 1.3.0
- license: MIT
- for-each
- version: 0.3.3
- license: MIT
- function-bind
- version: 1.1.2
- license: MIT
- get-intrinsic
- version: 1.2.4
- license: MIT
- gopd
- version: 1.0.1
- license: MIT
- has-property-descriptors
- version: 1.0.2
- license: MIT
- has-proto
- version: 1.0.3
- license: MIT
- has-symbols
- version: 1.0.3
- license: MIT
- has-tostringtag
- version: 1.0.2
- license: MIT
- hasown
- version: 2.0.1
- license: MIT
- inherits
- version: 2.0.4
- license: ISC
- is-arguments
- version: 1.1.1
- license: MIT
- is-callable
- version: 1.2.7
- license: MIT
- is-generator-function
- version: 1.0.10
- license: MIT
- is-nan
- version: 1.3.2
- license: MIT
- is-typed-array
- version: 1.1.13
- license: MIT
- object-is
- version: 1.1.5
- license: MIT
- object-keys
- version: 1.1.1
- license: MIT
- object.assign
- version: 4.1.5
- license: MIT
- possible-typed-array-names
- version: 1.0.0
- license: MIT
- process
- version: 0.11.10
- license: MIT
- set-function-length
- version: 1.2.1
- license: MIT
- util
- version: 0.12.5
- license: MIT
- webpack
- version: 5.91.0
- license: MIT
- which-typed-array
- version: 1.1.14
- license: MIT
- nextcloud
- version: 1.0.0
- license: AGPL-3.0-or-later

1
dist/core-ajax-cron.js.map vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/core-ajax-cron.js.map.license vendored Symbolic link
View file

@ -0,0 +1 @@
core-ajax-cron.js.license

4
dist/core-common.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,30 +9,30 @@ namespace Test\Command;
use OC\Core\Command\Background\Ajax;
use OC\Core\Command\Background\Cron;
use OC\Core\Command\Background\WebCron;
use OCP\IAppConfig;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\NullOutput;
use Test\TestCase;
class BackgroundJobsTest extends TestCase {
public function testCronCommand() {
$config = \OC::$server->getConfig();
$job = new Cron($config);
$appConfig = \OCP\Server::get(IAppConfig::class);
$job = new Cron($appConfig);
$job->run(new StringInput(''), new NullOutput());
$this->assertEquals('cron', $config->getAppValue('core', 'backgroundjobs_mode'));
$this->assertEquals('cron', $appConfig->getValueString('core', 'backgroundjobs_mode'));
}
public function testAjaxCommand() {
$config = \OC::$server->getConfig();
$job = new Ajax($config);
$appConfig = \OCP\Server::get(IAppConfig::class);
$job = new Ajax($appConfig);
$job->run(new StringInput(''), new NullOutput());
$this->assertEquals('ajax', $config->getAppValue('core', 'backgroundjobs_mode'));
$this->assertEquals('ajax', $appConfig->getValueString('core', 'backgroundjobs_mode'));
}
public function testWebCronCommand() {
$config = \OC::$server->getConfig();
$job = new WebCron($config);
$appConfig = \OCP\Server::get(IAppConfig::class);
$job = new WebCron($appConfig);
$job->run(new StringInput(''), new NullOutput());
$this->assertEquals('webcron', $config->getAppValue('core', 'backgroundjobs_mode'));
$this->assertEquals('webcron', $appConfig->getValueString('core', 'backgroundjobs_mode'));
}
}

View file

@ -11,6 +11,7 @@ module.exports = {
init: path.join(__dirname, 'apps/comments/src', 'init.ts'),
},
core: {
'ajax-cron': path.join(__dirname, 'core/src', 'ajax-cron.ts'),
files_client: path.join(__dirname, 'core/src', 'files/client.js'),
files_fileinfo: path.join(__dirname, 'core/src', 'files/fileinfo.js'),
install: path.join(__dirname, 'core/src', 'install.js'),