mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-10 12:48:29 -04:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26fa5d41a5 | ||
|
|
994086ceb2 |
No files matched your search
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
|
||||
occfg "github.com/opencloud-eu/opencloud/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/services/activitylog/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/services/activitylog/pkg/config/defaults"
|
||||
|
||||
@@ -35,8 +34,5 @@ func ParseConfig(cfg *config.Config) error {
|
||||
|
||||
// Validate validates the config
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.Events.Disabled && cfg.HTTP.Disabled {
|
||||
return shared.AllComponentsDisabledError(cfg.Service.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -10,21 +10,24 @@ file_delete)
|
||||
user 'user_id' trashed file 'item_id'
|
||||
file_trash_delete)
|
||||
user 'user_id' removed file 'item_id' from trashbin
|
||||
file_read)
|
||||
user 'user_id' read file 'item_id'
|
||||
```
|
||||
|
||||
Example json:
|
||||
```
|
||||
{"RemoteAddr":"","User":"user_id","URL":"","Method":"","UserAgent":"","Time":"","App":"admin_audit","Message":"user 'user_id' trashed file 'item_id'","Action":"file_delete","CLI":false,"Level":1,"Path":"path","Owner":"user_id","FileID":"item_id"}
|
||||
{"RemoteAddr":"","User":"user_id","URL":"","Method":"","UserAgent":"","Time":"","App":"admin_audit","Message":"user 'user_id' removed file 'item_id' from trashbin","Action":"file_trash_delete","CLI":false,"Level":1,"Path":"path","Owner":"user_id","FileID":"item_id"}
|
||||
{"RemoteAddr":"","User":"user_id","URL":"","Method":"","UserAgent":"","Time":"","App":"admin_audit","Message":"user 'user_id' read file 'item_id'","Action":"file_read","CLI":false,"Level":1,"Path":"path","Owner":"user_id","FileID":"item_id"}
|
||||
```
|
||||
|
||||
The audit service is not started automatically when running as single binary started via `opencloud server` or when running as docker container and must be started and stopped manually on demand.
|
||||
|
||||
The audit service logs:
|
||||
|
||||
- File system operations
|
||||
(create/delete/move; including actions on the trash bin and versioning)
|
||||
- User management operations
|
||||
- File system operations
|
||||
(create/delete/move/read; including actions on the trash bin and versioning)
|
||||
- User management operations
|
||||
(creation/deletion of users)
|
||||
- Sharing operations
|
||||
- Sharing operations
|
||||
(user/group sharing, sharing via link, changing permissions, calls to sharing API from clients)
|
||||
@@ -63,6 +63,10 @@ Store specific notes:
|
||||
- When using `nats-js-kv` it is recommended to set `OC_CACHE_STORE_NODES` to the same value as `OC_EVENTS_ENDPOINT`. That way the cache uses the same nats instance as the event bus.
|
||||
- When using the `nats-js-kv` store, it is possible to set `OC_CACHE_DISABLE_PERSISTENCE` to instruct nats to not persist cache data on disc.
|
||||
|
||||
### Auto-Accept Shares
|
||||
|
||||
When setting the `SHARING_AUTO_ACCEPT_SHARES` to `true` (sharing service), all incoming shares will be accepted automatically. Users can overwrite this setting individually in their profile. The deprecated `FRONTEND_AUTO_ACCEPT_SHARES` is still supported for backwards compatibility.
|
||||
|
||||
## Passwords
|
||||
|
||||
### The Password Policy
|
||||
|
||||
@@ -40,7 +40,11 @@ func ParseConfig(cfg *config.Config) error {
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.HTTP.Disabled && cfg.Events.DisabledConsumer {
|
||||
return shared.AllComponentsDisabledError(cfg.Service.Name)
|
||||
// might be debatable, but this situation should be treated as an error,
|
||||
// as the process wouldn't be able to serve either API and would thus be
|
||||
// completely useless -- in that case, just don't start this service
|
||||
// in the first place (especially since it's optional)
|
||||
return shared.AllComponentsDisabledError("graph")
|
||||
}
|
||||
|
||||
if cfg.TokenManager.JWTSecret == "" {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"errors"
|
||||
|
||||
occfg "github.com/opencloud-eu/opencloud/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/services/policies/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/services/policies/pkg/config/defaults"
|
||||
|
||||
@@ -34,8 +33,12 @@ func ParseConfig(cfg *config.Config) error {
|
||||
}
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.Events.Disabled && cfg.GRPC.Disabled {
|
||||
return shared.AllComponentsDisabledError(cfg.Service.Name)
|
||||
if cfg.GRPC.Disabled && cfg.Events.Disabled {
|
||||
// might be debatable, but this situation should be treated as an error,
|
||||
// as the process wouldn't be able to serve either API and would thus be
|
||||
// completely useless -- in that case, just don't start this service
|
||||
// in the first place (especially since it's optional)
|
||||
return errors.New("both gRPC and events APIs are disabled by configuration; at least one must be enabled")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -34,10 +34,6 @@ func ParseConfig(cfg *config.Config) error {
|
||||
}
|
||||
|
||||
func Validate(cfg *config.Config) error {
|
||||
if cfg.Events.Disabled && cfg.GRPC.Disabled {
|
||||
return shared.AllComponentsDisabledError(cfg.Service.Name)
|
||||
}
|
||||
|
||||
if cfg.TokenManager.JWTSecret == "" {
|
||||
return shared.MissingJWTTokenError(cfg.Service.Name)
|
||||
}
|
||||
|
||||
@@ -35,12 +35,7 @@ Share behavior can be configured via environment variables:
|
||||
- Auto-acceptance of shares
|
||||
- Share permissions and restrictions
|
||||
|
||||
### Auto-Accept Shares
|
||||
|
||||
When setting the `SHARING_AUTO_ACCEPT_SHARES` to `true` (sharing service), all
|
||||
incoming shares will be accepted automatically. Users can overwrite this
|
||||
setting individually in their profile. The deprecated
|
||||
`FRONTEND_AUTO_ACCEPT_SHARES` is still supported for backwards compatibility.
|
||||
See the `frontend` service README for more details on share-related configuration options.
|
||||
|
||||
## Scalability
|
||||
|
||||
|
||||
@@ -39,16 +39,25 @@ class WaitHelper {
|
||||
/**
|
||||
* Repeat $makeAttempt until $shouldStop returns true or the timeout elapses.
|
||||
*
|
||||
* @param callable $makeAttempt makes one attempt (e.g. sends a request) and returns its result
|
||||
* @param callable $shouldStop receives that result, returns true to stop polling
|
||||
* @param callable $makeAttempt makes one attempt (e.g. sends a request) and returns its result
|
||||
* @param callable $shouldStop receives that result, returns true to stop polling
|
||||
* @param int|null $intervalMs pause between attempts in ms; defaults to self::INTERVAL_MS
|
||||
* @param int|null $timeoutSeconds overall time to keep polling; defaults to self::TIMEOUT_SECONDS
|
||||
*
|
||||
* @return mixed the last result from $makeAttempt
|
||||
*/
|
||||
public static function waitUntil(callable $makeAttempt, callable $shouldStop): mixed {
|
||||
$deadline = \microtime(true) + self::TIMEOUT_SECONDS;
|
||||
public static function waitUntil(
|
||||
callable $makeAttempt,
|
||||
callable $shouldStop,
|
||||
?int $intervalMs = null,
|
||||
?int $timeoutSeconds = null
|
||||
): mixed {
|
||||
$intervalMs ??= self::INTERVAL_MS;
|
||||
$timeoutSeconds ??= self::TIMEOUT_SECONDS;
|
||||
$deadline = \microtime(true) + $timeoutSeconds;
|
||||
$result = $makeAttempt();
|
||||
while (!$shouldStop($result) && \microtime(true) < $deadline) {
|
||||
\usleep(self::INTERVAL_MS * 1000);
|
||||
\usleep($intervalMs * 1000);
|
||||
$result = $makeAttempt();
|
||||
}
|
||||
return $result;
|
||||
|
||||
@@ -150,7 +150,7 @@ class SearchContext implements Context {
|
||||
): void {
|
||||
// NOTE: because indexing of newly uploaded files or directories with OpenCloud is decoupled and occurs asynchronously
|
||||
// short wait is necessary before searching
|
||||
sleep(10);
|
||||
sleep(2);
|
||||
// remember the query so "should eventually contain" steps can re-search
|
||||
$this->lastSearchQuery = [
|
||||
"user" => $user,
|
||||
@@ -176,6 +176,25 @@ class SearchContext implements Context {
|
||||
string $path,
|
||||
string $user,
|
||||
TableNode $properties
|
||||
): void {
|
||||
$assert = fn () => $this->assertFileOrFolderInSearchResultContainsProperties($path, $user, $properties);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $user
|
||||
* @param TableNode $properties
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
private function assertFileOrFolderInSearchResultContainsProperties(
|
||||
string $path,
|
||||
string $user,
|
||||
TableNode $properties
|
||||
): void {
|
||||
$user = $this->featureContext->getActualUsername($user);
|
||||
$this->featureContext->verifyTableNodeColumns($properties, ['name', 'value']);
|
||||
@@ -234,8 +253,7 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the search result should contain these (?:files|entries) with highlight on keyword "([^"]*)"/')]
|
||||
public function theSearchResultShouldContainEntriesWithHighlight(
|
||||
private function assertSearchResultContainsEntriesWithHighlight(
|
||||
TableNode $expectedFiles,
|
||||
string $expectedContent
|
||||
): void {
|
||||
@@ -284,8 +302,15 @@ class SearchContext implements Context {
|
||||
): void {
|
||||
// NOTE: since indexing of newly uploaded files or directories with OpenCloud is decoupled and occurs asynchronously,
|
||||
// a short wait is necessary before searching
|
||||
sleep(5);
|
||||
$response = $this-> searchFiles($user, $pattern, null, $scopeType, $scope, $spaceName);
|
||||
sleep(2);
|
||||
$this->lastSearchQuery = [
|
||||
"user" => $user,
|
||||
"pattern" => $pattern,
|
||||
"scopeType" => $scopeType,
|
||||
"scope" => $scope,
|
||||
"spaceName" => $spaceName,
|
||||
];
|
||||
$response = $this->searchFiles($user, $pattern, null, $scopeType, $scope, $spaceName);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
@@ -300,7 +325,7 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function retrySearchUntilSatisfied(callable $assert): void {
|
||||
public function retrySearchUntilSatisfied(callable $assert): void {
|
||||
Assert::assertNotEmpty(
|
||||
$this->lastSearchQuery,
|
||||
'No search to retry. Use a "searches for ... using the WebDAV API" step first.'
|
||||
@@ -310,33 +335,67 @@ class SearchContext implements Context {
|
||||
fn () => $this->searchFiles(
|
||||
$query["user"],
|
||||
$query["pattern"],
|
||||
$query["limit"],
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$query["properties"]
|
||||
$query["limit"] ?? null,
|
||||
$query["scopeType"] ?? null,
|
||||
$query["scope"] ?? null,
|
||||
$query["spaceName"] ?? null,
|
||||
$query["properties"] ?? null
|
||||
),
|
||||
function ($response) use ($assert) {
|
||||
$this->featureContext->setResponse($response);
|
||||
try {
|
||||
$assert();
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
2000,
|
||||
20
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $numFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the search result should contain :numFiles files/entries')]
|
||||
public function theSearchResultShouldContainNumEntries(int $numFiles): void {
|
||||
$assert = fn () => $this->featureContext->checkIFResponseContainsNumberEntries($numFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $user
|
||||
* @param int $expectedNumber
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the search result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
public function theSearchResultShouldContainAnyOfTheseEntries(
|
||||
string $user,
|
||||
int $expectedNumber,
|
||||
TableNode $expectedFiles
|
||||
): void {
|
||||
$assert = fn () => $this->featureContext->checkIfSearchResultContainsFiles($user, $expectedNumber, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should eventually contain only these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldEventuallyContainOnlyEntries(string $user, TableNode $expectedFiles): void {
|
||||
#[Then('/^the search result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldContainOnlyEntries(string $user, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainOnlyEntries($user, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
@@ -348,9 +407,31 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should eventually contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldEventuallyContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainEntries($user, '', $expectedFiles);
|
||||
#[Then('/^the search result of user "([^"]*)" should contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$this->assertSearchResultContainsEntries($user, "", $expectedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result of user "([^"]*)" should not contain these (?:files|entries):$/')]
|
||||
public function theSearchResultShouldNotContainEntries(string $user, TableNode $expectedFiles): void {
|
||||
$this->assertSearchResultContainsEntries($user, "not", $expectedFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
* @param string $shouldOrNot (not|)
|
||||
* @param TableNode $expectedFiles
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function assertSearchResultContainsEntries(string $user, string $shouldOrNot, TableNode $expectedFiles): void {
|
||||
$assert = fn () => $this->featureContext->thePropfindResultShouldContainEntries($user, $shouldOrNot, $expectedFiles);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
@@ -361,9 +442,12 @@ class SearchContext implements Context {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('/^the search result should eventually contain these (?:files|entries) with highlight on keyword "([^"]*)"$/')]
|
||||
public function theSearchResultShouldEventuallyContainEntriesWithHighlight(TableNode $expectedFiles, string $expectedContent): void {
|
||||
$assert = fn () => $this->theSearchResultShouldContainEntriesWithHighlight($expectedFiles, $expectedContent);
|
||||
#[Then('/^the search result should contain these (?:files|entries) with highlight on keyword "([^"]*)"$/')]
|
||||
public function theSearchResultShouldContainEntriesWithHighlight(
|
||||
TableNode $expectedFiles,
|
||||
string $expectedContent
|
||||
): void {
|
||||
$assert = fn () => $this->assertSearchResultContainsEntriesWithHighlight($expectedFiles, $expectedContent);
|
||||
$this->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ class SpacesContext implements Context {
|
||||
private ChecksumContext $checksumContext;
|
||||
private FilesVersionsContext $filesVersionsContext;
|
||||
private ArchiverContext $archiverContext;
|
||||
private SearchContext $searchContext;
|
||||
|
||||
/**
|
||||
* key is space name and value is the username that created the space
|
||||
@@ -512,6 +513,7 @@ class SpacesContext implements Context {
|
||||
$this->checksumContext = BehatHelper::getContext($scope, $environment, 'ChecksumContext');
|
||||
$this->filesVersionsContext = BehatHelper::getContext($scope, $environment, 'FilesVersionsContext');
|
||||
$this->archiverContext = BehatHelper::getContext($scope, $environment, 'ArchiverContext');
|
||||
$this->searchContext = BehatHelper::getContext($scope, $environment, 'SearchContext');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3925,41 +3927,47 @@ class SpacesContext implements Context {
|
||||
*/
|
||||
#[Then('for user :user the search result should contain space :spaceName')]
|
||||
public function searchResultShouldContainSpace(string $user, string $spaceName): void {
|
||||
$responseArray = json_decode(
|
||||
json_encode(
|
||||
HttpRequestHelper::getResponseXml($this->featureContext->getResponse())->xpath("//d:response/d:href")
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
Assert::assertNotEmpty($responseArray, "search result is empty");
|
||||
// searching a space by name goes through the asynchronous file index, so
|
||||
// re-run the search until the space shows up (or the timeout elapses).
|
||||
$assert = function () use ($user, $spaceName): void {
|
||||
$responseArray = json_decode(
|
||||
json_encode(
|
||||
HttpRequestHelper::getResponseXml($this->featureContext->getResponse())->xpath("//d:response/d:href")
|
||||
),
|
||||
true,
|
||||
512,
|
||||
JSON_THROW_ON_ERROR
|
||||
);
|
||||
Assert::assertNotEmpty($responseArray, "search result is empty");
|
||||
|
||||
// for mountpoint, id looks a little different than for project space
|
||||
if (str_contains($spaceName, 'mountpoint')) {
|
||||
$splitSpaceName = explode("/", $spaceName);
|
||||
$space = $this->getSpaceByName($user, $splitSpaceName[1]);
|
||||
$splitSpaceId = explode("$", $space['id']);
|
||||
$spaceId = str_replace('!', '%21', $splitSpaceId[1]);
|
||||
} else {
|
||||
$space = $this->getSpaceByName($user, $spaceName);
|
||||
$spaceId = $space['id'];
|
||||
}
|
||||
$suffixPath = $user;
|
||||
$davPathVersion = $this->featureContext->getDavPathVersion();
|
||||
if ($davPathVersion === WebDavHelper::DAV_VERSION_SPACES) {
|
||||
$suffixPath = $spaceId;
|
||||
}
|
||||
|
||||
$topWebDavPath = "/" . WebDavHelper::getDavPath($davPathVersion, $suffixPath);
|
||||
|
||||
$spaceFound = false;
|
||||
foreach ($responseArray as $value) {
|
||||
if ($topWebDavPath === $value[0]) {
|
||||
$spaceFound = true;
|
||||
// for mountpoint, id looks a little different than for project space
|
||||
if (str_contains($spaceName, 'mountpoint')) {
|
||||
$splitSpaceName = explode("/", $spaceName);
|
||||
$space = $this->getSpaceByName($user, $splitSpaceName[1]);
|
||||
$splitSpaceId = explode("$", $space['id']);
|
||||
$spaceId = str_replace('!', '%21', $splitSpaceId[1]);
|
||||
} else {
|
||||
$space = $this->getSpaceByName($user, $spaceName);
|
||||
$spaceId = $space['id'];
|
||||
}
|
||||
}
|
||||
Assert::assertTrue($spaceFound, "response does not contain the space '$spaceName'");
|
||||
$suffixPath = $user;
|
||||
$davPathVersion = $this->featureContext->getDavPathVersion();
|
||||
if ($davPathVersion === WebDavHelper::DAV_VERSION_SPACES) {
|
||||
$suffixPath = $spaceId;
|
||||
}
|
||||
|
||||
$topWebDavPath = "/" . WebDavHelper::getDavPath($davPathVersion, $suffixPath);
|
||||
|
||||
$spaceFound = false;
|
||||
foreach ($responseArray as $value) {
|
||||
if ($topWebDavPath === $value[0]) {
|
||||
$spaceFound = true;
|
||||
}
|
||||
}
|
||||
Assert::assertTrue($spaceFound, "response does not contain the space '$spaceName'");
|
||||
};
|
||||
$this->searchContext->retrySearchUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,7 @@ use Behat\Gherkin\Node\TableNode;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TestHelpers\GraphHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\Then;
|
||||
@@ -38,6 +39,7 @@ require_once 'bootstrap.php';
|
||||
class TagContext implements Context {
|
||||
private FeatureContext $featureContext;
|
||||
private SpacesContext $spacesContext;
|
||||
private array $lastTagsQuery = [];
|
||||
|
||||
/**
|
||||
* This will run before EVERY scenario.
|
||||
@@ -175,19 +177,79 @@ class TagContext implements Context {
|
||||
*/
|
||||
#[When('user :user lists all available tag(s) via the Graph API')]
|
||||
public function theUserGetsAllAvailableTags(string $user): void {
|
||||
// Note: after creating or deleting tags, in some cases tags do not appear or disappear immediately,
|
||||
// So wait is necessary before listing tags
|
||||
sleep(5);
|
||||
$this->featureContext->setResponse(
|
||||
GraphHelper::getTags(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$this->featureContext->getStepLineRef()
|
||||
)
|
||||
// Note: after creating or deleting tags, in some cases tags do not appear or disappear immediately
|
||||
sleep(2);
|
||||
$this->lastTagsQuery = ["user" => $user];
|
||||
$this->featureContext->setResponse($this->fetchTags($user));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $user
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @throws Exception
|
||||
*/
|
||||
private function fetchTags(string $user): ResponseInterface {
|
||||
return GraphHelper::getTags(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
$user,
|
||||
$this->featureContext->getPasswordForUser($user),
|
||||
$this->featureContext->getStepLineRef()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* re-run the last tag listing until the assertion passes or the WaitHelper
|
||||
*
|
||||
* @param callable $assert
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function retryTagsUntilSatisfied(callable $assert): void {
|
||||
Assert::assertNotEmpty(
|
||||
$this->lastTagsQuery,
|
||||
'No tag listing to retry. Use a "lists all available tags via the Graph API" step first.'
|
||||
);
|
||||
$query = $this->lastTagsQuery;
|
||||
$response = WaitHelper::waitUntil(
|
||||
fn () => $this->fetchTags($query["user"]),
|
||||
function ($response) use ($assert) {
|
||||
$this->featureContext->setResponse($response);
|
||||
try {
|
||||
$assert();
|
||||
return true;
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should contain following tags:$/')]
|
||||
public function theResponseShouldContainFollowingTags(TableNode $table): void {
|
||||
$this->assertResponseContainsFollowingTags("", $table);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param TableNode $table
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should not contain following tags:$/')]
|
||||
public function theResponseShouldNotContainFollowingTags(TableNode $table): void {
|
||||
$this->assertResponseContainsFollowingTags("not", $table);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $shouldOrNot (not|)
|
||||
@@ -196,27 +258,29 @@ class TagContext implements Context {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the response should (not|)\\s?contain following tag(s):$/')]
|
||||
public function theFollowingTagsShouldExistForUser(string $shouldOrNot, TableNode $table): void {
|
||||
$rows = $table->getRows();
|
||||
foreach ($rows as $row) {
|
||||
private function assertResponseContainsFollowingTags(string $shouldOrNot, TableNode $table): void {
|
||||
$assert = function () use ($shouldOrNot, $table): void {
|
||||
$responseArray = $this->featureContext->getJsonDecodedResponse(
|
||||
$this->featureContext->getResponse()
|
||||
)['value'];
|
||||
if ($shouldOrNot === "not") {
|
||||
Assert::assertFalse(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response should not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
} else {
|
||||
Assert::assertTrue(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response does not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
foreach ($table->getRows() as $row) {
|
||||
if ($shouldOrNot === "not") {
|
||||
Assert::assertFalse(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response should not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
} else {
|
||||
Assert::assertTrue(
|
||||
\in_array($row[0], $responseArray),
|
||||
"the response does not contain the tag $row[0].\nResponse\n"
|
||||
. print_r($responseArray, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$this->retryTagsUntilSatisfied($assert);
|
||||
$assert();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4014,7 +4014,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the (?:propfind|search) result of user "([^"]*)" should (not|)\\s?contain these (?:files|entries):$/')]
|
||||
#[Then('/^the propfind result of user "([^"]*)" should (not|)\\s?contain these (?:files|entries):$/')]
|
||||
public function thePropfindResultShouldContainEntries(
|
||||
string $user,
|
||||
string $shouldOrNot,
|
||||
@@ -4036,7 +4036,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('/^the (?:propfind|search) result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
#[Then('/^the propfind result of user "([^"]*)" should contain only these (?:files|entries):$/')]
|
||||
public function thePropfindResultShouldContainOnlyEntries(
|
||||
string $user,
|
||||
TableNode $expectedFiles
|
||||
@@ -4063,7 +4063,7 @@ trait WebDav {
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
#[Then('the propfind/search result should contain :numFiles files/entries')]
|
||||
#[Then('the propfind result should contain :numFiles files/entries')]
|
||||
public function propfindResultShouldContainNumEntries(int $numFiles): void {
|
||||
$this->checkIFResponseContainsNumberEntries($numFiles);
|
||||
}
|
||||
@@ -4110,7 +4110,7 @@ trait WebDav {
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
#[Then('the propfind/search result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
#[Then('the propfind result of user :user should contain any :expectedNumber of these files/entries:')]
|
||||
public function theSearchResultOfUserShouldContainAnyOfTheseEntries(
|
||||
string $user,
|
||||
int $expectedNumber,
|
||||
|
||||
@@ -186,15 +186,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
- [apiSearch1/search.feature:438](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L438)
|
||||
- [apiSearch1/search.feature:439](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L439)
|
||||
- [apiSearch1/search.feature:465](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L465)
|
||||
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
|
||||
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
|
||||
|
||||
## Scenarios from core API tests that are expected to fail with decomposed storage
|
||||
|
||||
### File
|
||||
|
||||
@@ -186,15 +186,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
- [apiSearch1/search.feature:438](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L438)
|
||||
- [apiSearch1/search.feature:439](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L439)
|
||||
- [apiSearch1/search.feature:465](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L465)
|
||||
- [apiSearch1/search.feature:466](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L466)
|
||||
- [apiSearch1/search.feature:467](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L467)
|
||||
|
||||
## Scenarios from core API tests that are expected to fail with posix storage
|
||||
|
||||
### File
|
||||
|
||||
@@ -409,7 +409,7 @@ Feature: Search
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-4712 @issue-9780 @issue-9781 @issue-9783 @issue-10329
|
||||
@skip @issue-3501 @issue-9780
|
||||
Scenario Outline: report extra properties in search entries for a file
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "*insideTheFo*" using the WebDAV API requesting these properties:
|
||||
@@ -438,7 +438,7 @@ Feature: Search
|
||||
| new |
|
||||
| spaces |
|
||||
|
||||
@issue-4712 @issue-9780 @issue-9781 @issue-9783 @issue-10329
|
||||
@skip @issue-3501 @issue-9780
|
||||
Scenario Outline: report extra properties in search entries for a folder
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "*folderMain*" using the WebDAV API requesting these properties:
|
||||
|
||||
@@ -16,7 +16,7 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "namaste from nepal" to "hello.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -34,15 +34,15 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "alan@example.org want to say hello" to "findByEmail.docs"
|
||||
When user "Alice" searches for "Content:k6" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| wordWithNumber.md |
|
||||
When user "Alice" searches for "Content:https://opencloud.eu/" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| findByWebSite.txt |
|
||||
When user "Alice" searches for "Content:alan@" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| findByEmail.docs |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -71,11 +71,11 @@ Feature: content search
|
||||
And user "Alice" has uploaded file with content "He has expirience, we must to have, I have to find ...." to "fileWithStopWords.txt"
|
||||
When user "Alice" searches for 'Content:"he has"' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| fileWithStopWords.txt |
|
||||
When user "Alice" searches for 'Content:"I have"' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| fileWithStopWords.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -101,7 +101,7 @@ Feature: content search
|
||||
And user "Brian" has a share "uploadFolder" synced
|
||||
When user "Brian" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Brian" should eventually contain only these files:
|
||||
And the search result of user "Brian" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -121,7 +121,7 @@ Feature: content search
|
||||
And user "Alice" has deleted file "keywordAtLast.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
Examples:
|
||||
@@ -139,7 +139,7 @@ Feature: content search
|
||||
And user "Alice" has restored the file with original path "keywordAtStart.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -154,7 +154,7 @@ Feature: content search
|
||||
And user "Alice" has restored version index "1" of file "test.txt"
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| test.txt |
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -175,7 +175,7 @@ Feature: content search
|
||||
And using <dav-path-version> DAV path
|
||||
When user "Alice" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -204,7 +204,7 @@ Feature: content search
|
||||
And using <dav-path-version> DAV path
|
||||
When user "Brian" searches for "Content:hello" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain only these files:
|
||||
And the search result of user "Alice" should contain only these files:
|
||||
| keywordAtStart.txt |
|
||||
| keywordAtMiddle.txt |
|
||||
| keywordAtLast.txt |
|
||||
@@ -224,7 +224,7 @@ Feature: content search
|
||||
| technical task.txt | test |
|
||||
When user "Alice" searches for '<pattern>' using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result of user "Alice" should eventually contain these entries:
|
||||
And the search result of user "Alice" should contain these entries:
|
||||
| <search-result-1> |
|
||||
| <search-result-2> |
|
||||
And the search result should contain "<result-count>" entries
|
||||
@@ -251,7 +251,7 @@ Feature: content search
|
||||
And user "Alice" has uploaded a file inside space "project-space" with content "this is a simple odt file" to "test-odt-file.odt"
|
||||
When user "Alice" searches for "Content:simple" using the WebDAV API
|
||||
Then the HTTP status code should be "207"
|
||||
And the search result should eventually contain these entries with highlight on keyword "simple"
|
||||
And the search result should contain these entries with highlight on keyword "simple"
|
||||
| test-text-file.txt |
|
||||
| test-pdf-file.pdf |
|
||||
| test-cpp-file.cpp |
|
||||
|
||||
Reference in new issue
Block a user