Passed
Pull Request — master (#1141)
by Pauli
04:01
created

AdvSearchController::search()   B

Complexity

Conditions 8
Paths 17

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 8
eloc 18
c 2
b 0
f 0
nc 17
nop 6
dl 0
loc 26
rs 8.4444
1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Pauli Järvinen <[email protected]>
10
 * @copyright Pauli Järvinen 2024
11
 */
12
13
namespace OCA\Music\Controller;
14
15
use OCP\AppFramework\Controller;
16
use OCP\AppFramework\Http;
17
use OCP\AppFramework\Http\JSONResponse;
18
use OCP\IRequest;
19
20
use OCA\Music\AppFramework\BusinessLayer\BusinessLayerException;
21
use OCA\Music\AppFramework\Core\Logger;
22
use OCA\Music\BusinessLayer\AlbumBusinessLayer;
23
use OCA\Music\BusinessLayer\ArtistBusinessLayer;
24
use OCA\Music\BusinessLayer\BookmarkBusinessLayer;
25
use OCA\Music\BusinessLayer\GenreBusinessLayer;
26
use OCA\Music\BusinessLayer\PlaylistBusinessLayer;
27
use OCA\Music\BusinessLayer\PodcastChannelBusinessLayer;
28
use OCA\Music\BusinessLayer\PodcastEpisodeBusinessLayer;
29
use OCA\Music\BusinessLayer\RadioStationBusinessLayer;
30
use OCA\Music\BusinessLayer\TrackBusinessLayer;
31
use OCA\Music\Db\SortBy;
32
use OCA\Music\Http\ErrorResponse;
33
use OCA\Music\Utility\Random;
34
use OCA\Music\Utility\Util;
35
36
class AdvSearchController extends Controller {
37
38
	private $albumBusinessLayer;
39
	private $artistBusinessLayer;
40
	private $bookmarkBusinessLayer;
41
	private $genreBusinessLayer;
42
	private $playlistBusinessLayer;
43
	private $podcastChannelBusinessLayer;
44
	private $podcastEpisodeBusinessLayer;
45
	private $radioStationBusinessLayer;
46
	private $trackBusinessLayer;
47
	private $userId;
48
	private $random;
49
	private $logger;
50
51
	public function __construct(string $appName,
52
								IRequest $request,
53
								AlbumBusinessLayer $albumBusinessLayer,
54
								ArtistBusinessLayer $artistBusinessLayer,
55
								BookmarkBusinessLayer $bookmarkBusinessLayer,
56
								GenreBusinessLayer $genreBusinessLayer,
57
								PlaylistBusinessLayer $playlistBusinessLayer,
58
								PodcastChannelBusinessLayer $podcastChannelBusinessLayer,
59
								PodcastEpisodeBusinessLayer $podcastEpisodeBusinessLayer,
60
								RadioStationBusinessLayer $radioStationBusinessLayer,
61
								TrackBusinessLayer $trackBusinessLayer,
62
								?string $userId, // null if this gets called after the user has logged out or on a public page
63
								Random $random,
64
								Logger $logger) {
65
		parent::__construct($appName, $request);
66
		$this->albumBusinessLayer = $albumBusinessLayer;
67
		$this->artistBusinessLayer = $artistBusinessLayer;
68
		$this->bookmarkBusinessLayer = $bookmarkBusinessLayer;
69
		$this->genreBusinessLayer = $genreBusinessLayer;
70
		$this->playlistBusinessLayer = $playlistBusinessLayer;
71
		$this->podcastChannelBusinessLayer = $podcastChannelBusinessLayer;
72
		$this->podcastEpisodeBusinessLayer = $podcastEpisodeBusinessLayer;
73
		$this->radioStationBusinessLayer = $radioStationBusinessLayer;
74
		$this->trackBusinessLayer = $trackBusinessLayer;
75
		$this->userId = $userId;
76
		$this->random = $random;
77
		$this->logger = $logger;
78
	}
79
80
	/**
81
	 * @NoAdminRequired
82
	 * @NoCSRFRequired
83
	 */
84
	public function search(string $entity, string $rules, string $conjunction='and', string $order='name', ?int $limit=null, ?int $offset=null) {
85
		$rules = \json_decode($rules, true);
86
87
		foreach ($rules as $rule) {
88
			if (empty($rule['rule'] || empty($rule['operator'] || !isset($rule['input'])))) {
89
				return new ErrorResponse(Http::STATUS_BAD_REQUEST, 'Invalid search rule');
90
			}
91
		}
92
93
		try {
94
			$businessLayer = $this->businessLayerForType($entity);
95
96
			if ($businessLayer !== null) {
97
				\assert($this->userId !== null, 'Unexpected error: AdvSearch run with userId === null');
98
				$entities = $businessLayer->findAllAdvanced(
99
					$conjunction, $rules, $this->userId, self::mapSortBy($order), ($order==='random') ? $this->random : null, $limit, $offset);
100
				$entityIds = Util::extractIds($entities);
101
				return new JSONResponse([
102
					'id' => \md5($entity.\serialize($entityIds)), // use hash => identical results will have identical ID
103
					Util::snakeToCamelCase($entity).'Ids' => $entityIds
104
				]);
105
			} else {
106
				return new ErrorResponse(Http::STATUS_BAD_REQUEST, "Entity type '$entity' is not supported");
107
			}
108
		} catch (BusinessLayerException $e) {
109
			return new ErrorResponse(Http::STATUS_BAD_REQUEST, $e->getMessage());
110
		}
111
	}
112
113
	private function businessLayerForType($type) {
114
		$map = [
115
			'album' => $this->albumBusinessLayer,
116
			'artist' => $this->artistBusinessLayer,
117
			'bookmark' => $this->bookmarkBusinessLayer,
118
			'genre' => $this->genreBusinessLayer,
119
			'playlist' => $this->playlistBusinessLayer,
120
			'podcast_channel' => $this->podcastChannelBusinessLayer,
121
			'podcast_episode' => $this->podcastEpisodeBusinessLayer,
122
			'radio' => $this->radioStationBusinessLayer,
123
			'track' => $this->trackBusinessLayer,
124
		];
125
		return $map[$type] ?? null;
126
	}
127
128
	private static function mapSortBy(string $order) {
129
		$map = [
130
			'name'			=> SortBy::Name,
131
			'parent'		=> SortBy::Parent,
132
			'newest'		=> SortBy::Newest,
133
			'play_count'	=> SortBy::PlayCount,
134
			'last_played'	=> SortBy::LastPlayed,
135
			'rating'		=> SortBy::Rating,
136
		];
137
		return $map[$order] ?? SortBy::Name;
138
	}
139
140
}