mirror of
https://github.com/immich-app/immich.git
synced 2026-07-24 14:02:48 +03:00
Compare commits
12 Commits
make-sure-
...
refactor/s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b6e911e8 | ||
|
|
7a5f8d954a | ||
|
|
43b7c669e4 | ||
|
|
0231169135 | ||
|
|
1a13dff3c1 | ||
|
|
3a7bb760d3 | ||
|
|
702a14cd76 | ||
|
|
ae428c20a8 | ||
|
|
55aa05b4c9 | ||
|
|
fc4edf8d06 | ||
|
|
4f5e3cc234 | ||
|
|
2f83615195 |
19
mobile/openapi/README.md
generated
19
mobile/openapi/README.md
generated
@@ -148,6 +148,7 @@ Class | Method | HTTP request | Description
|
||||
*DeprecatedApi* | [**createPartnerDeprecated**](doc//DeprecatedApi.md#createpartnerdeprecated) | **POST** /partners/{id} | Create a partner
|
||||
*DeprecatedApi* | [**getQueuesLegacy**](doc//DeprecatedApi.md#getqueueslegacy) | **GET** /jobs | Retrieve queue counts and status
|
||||
*DeprecatedApi* | [**runQueueCommandLegacy**](doc//DeprecatedApi.md#runqueuecommandlegacy) | **PUT** /jobs/{name} | Run jobs
|
||||
*DeprecatedApi* | [**searchLargeAssets**](doc//DeprecatedApi.md#searchlargeassets) | **POST** /search/large-assets | Search large assets
|
||||
*DeprecatedApi* | [**updateApiKey**](doc//DeprecatedApi.md#updateapikey) | **PUT** /api-keys/{id} | Update an API key
|
||||
*DeprecatedApi* | [**updateAsset**](doc//DeprecatedApi.md#updateasset) | **PUT** /assets/{id} | Update an asset
|
||||
*DeprecatedApi* | [**updateAssets**](doc//DeprecatedApi.md#updateassets) | **PUT** /assets | Update assets
|
||||
@@ -414,6 +415,7 @@ Class | Method | HTTP request | Description
|
||||
- [AudioCodec](doc//AudioCodec.md)
|
||||
- [AuthStatusResponseDto](doc//AuthStatusResponseDto.md)
|
||||
- [AvatarUpdate](doc//AvatarUpdate.md)
|
||||
- [BoolFilter](doc//BoolFilter.md)
|
||||
- [BulkIdErrorReason](doc//BulkIdErrorReason.md)
|
||||
- [BulkIdResponseDto](doc//BulkIdResponseDto.md)
|
||||
- [BulkIdsDto](doc//BulkIdsDto.md)
|
||||
@@ -435,6 +437,8 @@ Class | Method | HTTP request | Description
|
||||
- [DatabaseBackupDeleteDto](doc//DatabaseBackupDeleteDto.md)
|
||||
- [DatabaseBackupDto](doc//DatabaseBackupDto.md)
|
||||
- [DatabaseBackupListResponseDto](doc//DatabaseBackupListResponseDto.md)
|
||||
- [DateFilter](doc//DateFilter.md)
|
||||
- [DateFilterNullable](doc//DateFilterNullable.md)
|
||||
- [DownloadArchiveDto](doc//DownloadArchiveDto.md)
|
||||
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
|
||||
- [DownloadInfoDto](doc//DownloadInfoDto.md)
|
||||
@@ -447,12 +451,17 @@ Class | Method | HTTP request | Description
|
||||
- [DuplicateResponseDto](doc//DuplicateResponseDto.md)
|
||||
- [EmailNotificationsResponse](doc//EmailNotificationsResponse.md)
|
||||
- [EmailNotificationsUpdate](doc//EmailNotificationsUpdate.md)
|
||||
- [EnumFilterAssetType](doc//EnumFilterAssetType.md)
|
||||
- [EnumFilterAssetVisibility](doc//EnumFilterAssetVisibility.md)
|
||||
- [ExifResponseDto](doc//ExifResponseDto.md)
|
||||
- [FaceDto](doc//FaceDto.md)
|
||||
- [FacialRecognitionConfig](doc//FacialRecognitionConfig.md)
|
||||
- [FoldersResponse](doc//FoldersResponse.md)
|
||||
- [FoldersUpdate](doc//FoldersUpdate.md)
|
||||
- [HlsVideoResolution](doc//HlsVideoResolution.md)
|
||||
- [IdFilter](doc//IdFilter.md)
|
||||
- [IdFilterNullable](doc//IdFilterNullable.md)
|
||||
- [IdsFilter](doc//IdsFilter.md)
|
||||
- [ImageFormat](doc//ImageFormat.md)
|
||||
- [IntegrityReport](doc//IntegrityReport.md)
|
||||
- [IntegrityReportResponseDto](doc//IntegrityReportResponseDto.md)
|
||||
@@ -497,6 +506,8 @@ Class | Method | HTTP request | Description
|
||||
- [NotificationType](doc//NotificationType.md)
|
||||
- [NotificationUpdateAllDto](doc//NotificationUpdateAllDto.md)
|
||||
- [NotificationUpdateDto](doc//NotificationUpdateDto.md)
|
||||
- [NumberFilter](doc//NumberFilter.md)
|
||||
- [NumberFilterNullable](doc//NumberFilterNullable.md)
|
||||
- [OAuthAuthorizeResponseDto](doc//OAuthAuthorizeResponseDto.md)
|
||||
- [OAuthCallbackDto](doc//OAuthCallbackDto.md)
|
||||
- [OAuthConfigDto](doc//OAuthConfigDto.md)
|
||||
@@ -559,6 +570,10 @@ Class | Method | HTTP request | Description
|
||||
- [SearchExploreResponseDto](doc//SearchExploreResponseDto.md)
|
||||
- [SearchFacetCountResponseDto](doc//SearchFacetCountResponseDto.md)
|
||||
- [SearchFacetResponseDto](doc//SearchFacetResponseDto.md)
|
||||
- [SearchFilter](doc//SearchFilter.md)
|
||||
- [SearchFilterBranch](doc//SearchFilterBranch.md)
|
||||
- [SearchOrder](doc//SearchOrder.md)
|
||||
- [SearchOrderField](doc//SearchOrderField.md)
|
||||
- [SearchResponseDto](doc//SearchResponseDto.md)
|
||||
- [SearchStatisticsResponseDto](doc//SearchStatisticsResponseDto.md)
|
||||
- [SearchSuggestionType](doc//SearchSuggestionType.md)
|
||||
@@ -593,6 +608,10 @@ Class | Method | HTTP request | Description
|
||||
- [StackUpdateDto](doc//StackUpdateDto.md)
|
||||
- [StatisticsSearchDto](doc//StatisticsSearchDto.md)
|
||||
- [StorageFolder](doc//StorageFolder.md)
|
||||
- [StringFilter](doc//StringFilter.md)
|
||||
- [StringFilterNullable](doc//StringFilterNullable.md)
|
||||
- [StringPatternFilter](doc//StringPatternFilter.md)
|
||||
- [StringSimilarityFilter](doc//StringSimilarityFilter.md)
|
||||
- [SyncAckDeleteDto](doc//SyncAckDeleteDto.md)
|
||||
- [SyncAckDto](doc//SyncAckDto.md)
|
||||
- [SyncAckSetDto](doc//SyncAckSetDto.md)
|
||||
|
||||
18
mobile/openapi/lib/api.dart
generated
18
mobile/openapi/lib/api.dart
generated
@@ -135,6 +135,7 @@ part 'model/asset_visibility.dart';
|
||||
part 'model/audio_codec.dart';
|
||||
part 'model/auth_status_response_dto.dart';
|
||||
part 'model/avatar_update.dart';
|
||||
part 'model/bool_filter.dart';
|
||||
part 'model/bulk_id_error_reason.dart';
|
||||
part 'model/bulk_id_response_dto.dart';
|
||||
part 'model/bulk_ids_dto.dart';
|
||||
@@ -156,6 +157,8 @@ part 'model/database_backup_config.dart';
|
||||
part 'model/database_backup_delete_dto.dart';
|
||||
part 'model/database_backup_dto.dart';
|
||||
part 'model/database_backup_list_response_dto.dart';
|
||||
part 'model/date_filter.dart';
|
||||
part 'model/date_filter_nullable.dart';
|
||||
part 'model/download_archive_dto.dart';
|
||||
part 'model/download_archive_info.dart';
|
||||
part 'model/download_info_dto.dart';
|
||||
@@ -168,12 +171,17 @@ part 'model/duplicate_resolve_group_dto.dart';
|
||||
part 'model/duplicate_response_dto.dart';
|
||||
part 'model/email_notifications_response.dart';
|
||||
part 'model/email_notifications_update.dart';
|
||||
part 'model/enum_filter_asset_type.dart';
|
||||
part 'model/enum_filter_asset_visibility.dart';
|
||||
part 'model/exif_response_dto.dart';
|
||||
part 'model/face_dto.dart';
|
||||
part 'model/facial_recognition_config.dart';
|
||||
part 'model/folders_response.dart';
|
||||
part 'model/folders_update.dart';
|
||||
part 'model/hls_video_resolution.dart';
|
||||
part 'model/id_filter.dart';
|
||||
part 'model/id_filter_nullable.dart';
|
||||
part 'model/ids_filter.dart';
|
||||
part 'model/image_format.dart';
|
||||
part 'model/integrity_report.dart';
|
||||
part 'model/integrity_report_response_dto.dart';
|
||||
@@ -218,6 +226,8 @@ part 'model/notification_level.dart';
|
||||
part 'model/notification_type.dart';
|
||||
part 'model/notification_update_all_dto.dart';
|
||||
part 'model/notification_update_dto.dart';
|
||||
part 'model/number_filter.dart';
|
||||
part 'model/number_filter_nullable.dart';
|
||||
part 'model/o_auth_authorize_response_dto.dart';
|
||||
part 'model/o_auth_callback_dto.dart';
|
||||
part 'model/o_auth_config_dto.dart';
|
||||
@@ -280,6 +290,10 @@ part 'model/search_explore_item.dart';
|
||||
part 'model/search_explore_response_dto.dart';
|
||||
part 'model/search_facet_count_response_dto.dart';
|
||||
part 'model/search_facet_response_dto.dart';
|
||||
part 'model/search_filter.dart';
|
||||
part 'model/search_filter_branch.dart';
|
||||
part 'model/search_order.dart';
|
||||
part 'model/search_order_field.dart';
|
||||
part 'model/search_response_dto.dart';
|
||||
part 'model/search_statistics_response_dto.dart';
|
||||
part 'model/search_suggestion_type.dart';
|
||||
@@ -314,6 +328,10 @@ part 'model/stack_response_dto.dart';
|
||||
part 'model/stack_update_dto.dart';
|
||||
part 'model/statistics_search_dto.dart';
|
||||
part 'model/storage_folder.dart';
|
||||
part 'model/string_filter.dart';
|
||||
part 'model/string_filter_nullable.dart';
|
||||
part 'model/string_pattern_filter.dart';
|
||||
part 'model/string_similarity_filter.dart';
|
||||
part 'model/sync_ack_delete_dto.dart';
|
||||
part 'model/sync_ack_dto.dart';
|
||||
part 'model/sync_ack_set_dto.dart';
|
||||
|
||||
332
mobile/openapi/lib/api/deprecated_api.dart
generated
332
mobile/openapi/lib/api/deprecated_api.dart
generated
@@ -185,6 +185,338 @@ class DeprecatedApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Search large assets
|
||||
///
|
||||
/// Search for assets that are considered large based on specified criteria.
|
||||
///
|
||||
/// Note: This method returns the HTTP [Response].
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [List<String>] albumIds:
|
||||
/// Filter by album IDs
|
||||
///
|
||||
/// * [String] city:
|
||||
/// Filter by city name
|
||||
///
|
||||
/// * [String] country:
|
||||
/// Filter by country name
|
||||
///
|
||||
/// * [DateTime] createdAfter:
|
||||
/// Filter by creation date (after)
|
||||
///
|
||||
/// * [DateTime] createdBefore:
|
||||
/// Filter by creation date (before)
|
||||
///
|
||||
/// * [bool] isEncoded:
|
||||
/// Filter by encoded status
|
||||
///
|
||||
/// * [bool] isFavorite:
|
||||
/// Filter by favorite status
|
||||
///
|
||||
/// * [bool] isMotion:
|
||||
/// Filter by motion photo status
|
||||
///
|
||||
/// * [bool] isNotInAlbum:
|
||||
/// Filter assets not in any album
|
||||
///
|
||||
/// * [bool] isOffline:
|
||||
/// Filter by offline status
|
||||
///
|
||||
/// * [String] lensModel:
|
||||
/// Filter by lens model
|
||||
///
|
||||
/// * [String] libraryId:
|
||||
/// Library ID to filter by
|
||||
///
|
||||
/// * [String] make:
|
||||
/// Filter by camera make
|
||||
///
|
||||
/// * [int] minFileSize:
|
||||
/// Minimum file size in bytes
|
||||
///
|
||||
/// * [String] model:
|
||||
/// Filter by camera model
|
||||
///
|
||||
/// * [String] ocr:
|
||||
/// Filter by OCR text content
|
||||
///
|
||||
/// * [List<String>] personIds:
|
||||
/// Filter by person IDs
|
||||
///
|
||||
/// * [int] rating:
|
||||
/// Filter by rating [1-5], or null for unrated
|
||||
///
|
||||
/// * [int] size:
|
||||
/// Number of results to return
|
||||
///
|
||||
/// * [String] state:
|
||||
/// Filter by state/province name
|
||||
///
|
||||
/// * [List<String>] tagIds:
|
||||
/// Filter by tag IDs
|
||||
///
|
||||
/// * [DateTime] takenAfter:
|
||||
/// Filter by taken date (after)
|
||||
///
|
||||
/// * [DateTime] takenBefore:
|
||||
/// Filter by taken date (before)
|
||||
///
|
||||
/// * [DateTime] trashedAfter:
|
||||
/// Filter by trash date (after)
|
||||
///
|
||||
/// * [DateTime] trashedBefore:
|
||||
/// Filter by trash date (before)
|
||||
///
|
||||
/// * [AssetTypeEnum] type:
|
||||
///
|
||||
/// * [DateTime] updatedAfter:
|
||||
/// Filter by update date (after)
|
||||
///
|
||||
/// * [DateTime] updatedBefore:
|
||||
/// Filter by update date (before)
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
///
|
||||
/// * [bool] withDeleted:
|
||||
/// Include deleted assets
|
||||
///
|
||||
/// * [bool] withExif:
|
||||
/// Include EXIF data in response
|
||||
Future<Response> searchLargeAssetsWithHttpInfo({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, int? rating, int? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, Future<void>? abortTrigger, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final apiPath = r'/search/large-assets';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
if (albumIds != null) {
|
||||
queryParams.addAll(_queryParams('multi', 'albumIds', albumIds));
|
||||
}
|
||||
if (city != null) {
|
||||
queryParams.addAll(_queryParams('', 'city', city));
|
||||
}
|
||||
if (country != null) {
|
||||
queryParams.addAll(_queryParams('', 'country', country));
|
||||
}
|
||||
if (createdAfter != null) {
|
||||
queryParams.addAll(_queryParams('', 'createdAfter', createdAfter));
|
||||
}
|
||||
if (createdBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'createdBefore', createdBefore));
|
||||
}
|
||||
if (isEncoded != null) {
|
||||
queryParams.addAll(_queryParams('', 'isEncoded', isEncoded));
|
||||
}
|
||||
if (isFavorite != null) {
|
||||
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
|
||||
}
|
||||
if (isMotion != null) {
|
||||
queryParams.addAll(_queryParams('', 'isMotion', isMotion));
|
||||
}
|
||||
if (isNotInAlbum != null) {
|
||||
queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum));
|
||||
}
|
||||
if (isOffline != null) {
|
||||
queryParams.addAll(_queryParams('', 'isOffline', isOffline));
|
||||
}
|
||||
if (lensModel != null) {
|
||||
queryParams.addAll(_queryParams('', 'lensModel', lensModel));
|
||||
}
|
||||
if (libraryId != null) {
|
||||
queryParams.addAll(_queryParams('', 'libraryId', libraryId));
|
||||
}
|
||||
if (make != null) {
|
||||
queryParams.addAll(_queryParams('', 'make', make));
|
||||
}
|
||||
if (minFileSize != null) {
|
||||
queryParams.addAll(_queryParams('', 'minFileSize', minFileSize));
|
||||
}
|
||||
if (model != null) {
|
||||
queryParams.addAll(_queryParams('', 'model', model));
|
||||
}
|
||||
if (ocr != null) {
|
||||
queryParams.addAll(_queryParams('', 'ocr', ocr));
|
||||
}
|
||||
if (personIds != null) {
|
||||
queryParams.addAll(_queryParams('multi', 'personIds', personIds));
|
||||
}
|
||||
if (rating != null) {
|
||||
queryParams.addAll(_queryParams('', 'rating', rating));
|
||||
}
|
||||
if (size != null) {
|
||||
queryParams.addAll(_queryParams('', 'size', size));
|
||||
}
|
||||
if (state != null) {
|
||||
queryParams.addAll(_queryParams('', 'state', state));
|
||||
}
|
||||
if (tagIds != null) {
|
||||
queryParams.addAll(_queryParams('multi', 'tagIds', tagIds));
|
||||
}
|
||||
if (takenAfter != null) {
|
||||
queryParams.addAll(_queryParams('', 'takenAfter', takenAfter));
|
||||
}
|
||||
if (takenBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'takenBefore', takenBefore));
|
||||
}
|
||||
if (trashedAfter != null) {
|
||||
queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter));
|
||||
}
|
||||
if (trashedBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore));
|
||||
}
|
||||
if (type != null) {
|
||||
queryParams.addAll(_queryParams('', 'type', type));
|
||||
}
|
||||
if (updatedAfter != null) {
|
||||
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
|
||||
}
|
||||
if (updatedBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
|
||||
}
|
||||
if (visibility != null) {
|
||||
queryParams.addAll(_queryParams('', 'visibility', visibility));
|
||||
}
|
||||
if (withDeleted != null) {
|
||||
queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
|
||||
}
|
||||
if (withExif != null) {
|
||||
queryParams.addAll(_queryParams('', 'withExif', withExif));
|
||||
}
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
apiPath,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
abortTrigger: abortTrigger,
|
||||
);
|
||||
}
|
||||
|
||||
/// Search large assets
|
||||
///
|
||||
/// Search for assets that are considered large based on specified criteria.
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [List<String>] albumIds:
|
||||
/// Filter by album IDs
|
||||
///
|
||||
/// * [String] city:
|
||||
/// Filter by city name
|
||||
///
|
||||
/// * [String] country:
|
||||
/// Filter by country name
|
||||
///
|
||||
/// * [DateTime] createdAfter:
|
||||
/// Filter by creation date (after)
|
||||
///
|
||||
/// * [DateTime] createdBefore:
|
||||
/// Filter by creation date (before)
|
||||
///
|
||||
/// * [bool] isEncoded:
|
||||
/// Filter by encoded status
|
||||
///
|
||||
/// * [bool] isFavorite:
|
||||
/// Filter by favorite status
|
||||
///
|
||||
/// * [bool] isMotion:
|
||||
/// Filter by motion photo status
|
||||
///
|
||||
/// * [bool] isNotInAlbum:
|
||||
/// Filter assets not in any album
|
||||
///
|
||||
/// * [bool] isOffline:
|
||||
/// Filter by offline status
|
||||
///
|
||||
/// * [String] lensModel:
|
||||
/// Filter by lens model
|
||||
///
|
||||
/// * [String] libraryId:
|
||||
/// Library ID to filter by
|
||||
///
|
||||
/// * [String] make:
|
||||
/// Filter by camera make
|
||||
///
|
||||
/// * [int] minFileSize:
|
||||
/// Minimum file size in bytes
|
||||
///
|
||||
/// * [String] model:
|
||||
/// Filter by camera model
|
||||
///
|
||||
/// * [String] ocr:
|
||||
/// Filter by OCR text content
|
||||
///
|
||||
/// * [List<String>] personIds:
|
||||
/// Filter by person IDs
|
||||
///
|
||||
/// * [int] rating:
|
||||
/// Filter by rating [1-5], or null for unrated
|
||||
///
|
||||
/// * [int] size:
|
||||
/// Number of results to return
|
||||
///
|
||||
/// * [String] state:
|
||||
/// Filter by state/province name
|
||||
///
|
||||
/// * [List<String>] tagIds:
|
||||
/// Filter by tag IDs
|
||||
///
|
||||
/// * [DateTime] takenAfter:
|
||||
/// Filter by taken date (after)
|
||||
///
|
||||
/// * [DateTime] takenBefore:
|
||||
/// Filter by taken date (before)
|
||||
///
|
||||
/// * [DateTime] trashedAfter:
|
||||
/// Filter by trash date (after)
|
||||
///
|
||||
/// * [DateTime] trashedBefore:
|
||||
/// Filter by trash date (before)
|
||||
///
|
||||
/// * [AssetTypeEnum] type:
|
||||
///
|
||||
/// * [DateTime] updatedAfter:
|
||||
/// Filter by update date (after)
|
||||
///
|
||||
/// * [DateTime] updatedBefore:
|
||||
/// Filter by update date (before)
|
||||
///
|
||||
/// * [AssetVisibility] visibility:
|
||||
///
|
||||
/// * [bool] withDeleted:
|
||||
/// Include deleted assets
|
||||
///
|
||||
/// * [bool] withExif:
|
||||
/// Include EXIF data in response
|
||||
Future<List<AssetResponseDto>?> searchLargeAssets({ List<String>? albumIds, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, bool? isEncoded, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, String? lensModel, String? libraryId, String? make, int? minFileSize, String? model, String? ocr, List<String>? personIds, int? rating, int? size, String? state, List<String>? tagIds, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, AssetVisibility? visibility, bool? withDeleted, bool? withExif, Future<void>? abortTrigger, }) async {
|
||||
final response = await searchLargeAssetsWithHttpInfo(albumIds: albumIds, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, isEncoded: isEncoded, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, lensModel: lensModel, libraryId: libraryId, make: make, minFileSize: minFileSize, model: model, ocr: ocr, personIds: personIds, rating: rating, size: size, state: state, tagIds: tagIds, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, visibility: visibility, withDeleted: withDeleted, withExif: withExif, abortTrigger: abortTrigger,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetResponseDto>') as List)
|
||||
.cast<AssetResponseDto>()
|
||||
.toList(growable: false);
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Update an API key
|
||||
///
|
||||
/// Updates the name and permissions of an API key by its ID. The current user must own this API key.
|
||||
|
||||
36
mobile/openapi/lib/api_client.dart
generated
36
mobile/openapi/lib/api_client.dart
generated
@@ -315,6 +315,8 @@ class ApiClient {
|
||||
return AuthStatusResponseDto.fromJson(value);
|
||||
case 'AvatarUpdate':
|
||||
return AvatarUpdate.fromJson(value);
|
||||
case 'BoolFilter':
|
||||
return BoolFilter.fromJson(value);
|
||||
case 'BulkIdErrorReason':
|
||||
return BulkIdErrorReasonTypeTransformer().decode(value);
|
||||
case 'BulkIdResponseDto':
|
||||
@@ -357,6 +359,10 @@ class ApiClient {
|
||||
return DatabaseBackupDto.fromJson(value);
|
||||
case 'DatabaseBackupListResponseDto':
|
||||
return DatabaseBackupListResponseDto.fromJson(value);
|
||||
case 'DateFilter':
|
||||
return DateFilter.fromJson(value);
|
||||
case 'DateFilterNullable':
|
||||
return DateFilterNullable.fromJson(value);
|
||||
case 'DownloadArchiveDto':
|
||||
return DownloadArchiveDto.fromJson(value);
|
||||
case 'DownloadArchiveInfo':
|
||||
@@ -381,6 +387,10 @@ class ApiClient {
|
||||
return EmailNotificationsResponse.fromJson(value);
|
||||
case 'EmailNotificationsUpdate':
|
||||
return EmailNotificationsUpdate.fromJson(value);
|
||||
case 'EnumFilterAssetType':
|
||||
return EnumFilterAssetType.fromJson(value);
|
||||
case 'EnumFilterAssetVisibility':
|
||||
return EnumFilterAssetVisibility.fromJson(value);
|
||||
case 'ExifResponseDto':
|
||||
return ExifResponseDto.fromJson(value);
|
||||
case 'FaceDto':
|
||||
@@ -393,6 +403,12 @@ class ApiClient {
|
||||
return FoldersUpdate.fromJson(value);
|
||||
case 'HlsVideoResolution':
|
||||
return HlsVideoResolutionTypeTransformer().decode(value);
|
||||
case 'IdFilter':
|
||||
return IdFilter.fromJson(value);
|
||||
case 'IdFilterNullable':
|
||||
return IdFilterNullable.fromJson(value);
|
||||
case 'IdsFilter':
|
||||
return IdsFilter.fromJson(value);
|
||||
case 'ImageFormat':
|
||||
return ImageFormatTypeTransformer().decode(value);
|
||||
case 'IntegrityReport':
|
||||
@@ -481,6 +497,10 @@ class ApiClient {
|
||||
return NotificationUpdateAllDto.fromJson(value);
|
||||
case 'NotificationUpdateDto':
|
||||
return NotificationUpdateDto.fromJson(value);
|
||||
case 'NumberFilter':
|
||||
return NumberFilter.fromJson(value);
|
||||
case 'NumberFilterNullable':
|
||||
return NumberFilterNullable.fromJson(value);
|
||||
case 'OAuthAuthorizeResponseDto':
|
||||
return OAuthAuthorizeResponseDto.fromJson(value);
|
||||
case 'OAuthCallbackDto':
|
||||
@@ -605,6 +625,14 @@ class ApiClient {
|
||||
return SearchFacetCountResponseDto.fromJson(value);
|
||||
case 'SearchFacetResponseDto':
|
||||
return SearchFacetResponseDto.fromJson(value);
|
||||
case 'SearchFilter':
|
||||
return SearchFilter.fromJson(value);
|
||||
case 'SearchFilterBranch':
|
||||
return SearchFilterBranch.fromJson(value);
|
||||
case 'SearchOrder':
|
||||
return SearchOrder.fromJson(value);
|
||||
case 'SearchOrderField':
|
||||
return SearchOrderFieldTypeTransformer().decode(value);
|
||||
case 'SearchResponseDto':
|
||||
return SearchResponseDto.fromJson(value);
|
||||
case 'SearchStatisticsResponseDto':
|
||||
@@ -673,6 +701,14 @@ class ApiClient {
|
||||
return StatisticsSearchDto.fromJson(value);
|
||||
case 'StorageFolder':
|
||||
return StorageFolderTypeTransformer().decode(value);
|
||||
case 'StringFilter':
|
||||
return StringFilter.fromJson(value);
|
||||
case 'StringFilterNullable':
|
||||
return StringFilterNullable.fromJson(value);
|
||||
case 'StringPatternFilter':
|
||||
return StringPatternFilter.fromJson(value);
|
||||
case 'StringSimilarityFilter':
|
||||
return StringSimilarityFilter.fromJson(value);
|
||||
case 'SyncAckDeleteDto':
|
||||
return SyncAckDeleteDto.fromJson(value);
|
||||
case 'SyncAckDto':
|
||||
|
||||
3
mobile/openapi/lib/api_helper.dart
generated
3
mobile/openapi/lib/api_helper.dart
generated
@@ -172,6 +172,9 @@ String parameterToString(dynamic value) {
|
||||
if (value is ReleaseType) {
|
||||
return ReleaseTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is SearchOrderField) {
|
||||
return SearchOrderFieldTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is SearchSuggestionType) {
|
||||
return SearchSuggestionTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
||||
99
mobile/openapi/lib/model/bool_filter.dart
generated
Normal file
99
mobile/openapi/lib/model/bool_filter.dart
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class BoolFilter {
|
||||
/// Returns a new [BoolFilter] instance.
|
||||
BoolFilter({
|
||||
required this.eq,
|
||||
});
|
||||
|
||||
bool eq;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is BoolFilter &&
|
||||
other.eq == eq;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'BoolFilter[eq=$eq]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'eq'] = this.eq;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [BoolFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static BoolFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "BoolFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return BoolFilter(
|
||||
eq: mapValueOfType<bool>(json, r'eq')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<BoolFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <BoolFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = BoolFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, BoolFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, BoolFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = BoolFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of BoolFilter-objects as value to a dart map
|
||||
static Map<String, List<BoolFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<BoolFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = BoolFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'eq',
|
||||
};
|
||||
}
|
||||
|
||||
199
mobile/openapi/lib/model/date_filter.dart
generated
Normal file
199
mobile/openapi/lib/model/date_filter.dart
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class DateFilter {
|
||||
/// Returns a new [DateFilter] instance.
|
||||
DateFilter({
|
||||
this.eq = const Optional.absent(),
|
||||
this.gt = const Optional.absent(),
|
||||
this.gte = const Optional.absent(),
|
||||
this.lt = const Optional.absent(),
|
||||
this.lte = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> eq;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> gt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> gte;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> lt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> lte;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> ne;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DateFilter &&
|
||||
other.eq == eq &&
|
||||
other.gt == gt &&
|
||||
other.gte == gte &&
|
||||
other.lt == lt &&
|
||||
other.lte == lte &&
|
||||
other.ne == ne;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(gt == null ? 0 : gt!.hashCode) +
|
||||
(gte == null ? 0 : gte!.hashCode) +
|
||||
(lt == null ? 0 : lt!.hashCode) +
|
||||
(lte == null ? 0 : lte!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'DateFilter[eq=$eq, gt=$gt, gte=$gte, lt=$lt, lte=$lte, ne=$ne]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.gt.isPresent) {
|
||||
final value = this.gt.value;
|
||||
json[r'gt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.gte.isPresent) {
|
||||
final value = this.gte.value;
|
||||
json[r'gte'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.lt.isPresent) {
|
||||
final value = this.lt.value;
|
||||
json[r'lt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.lte.isPresent) {
|
||||
final value = this.lte.value;
|
||||
json[r'lte'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [DateFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static DateFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "DateFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return DateFilter(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapDateTime(json, r'eq', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
gt: json.containsKey(r'gt') ? Optional.present(mapDateTime(json, r'gt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
gte: json.containsKey(r'gte') ? Optional.present(mapDateTime(json, r'gte', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
lt: json.containsKey(r'lt') ? Optional.present(mapDateTime(json, r'lt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
lte: json.containsKey(r'lte') ? Optional.present(mapDateTime(json, r'lte', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapDateTime(json, r'ne', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<DateFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <DateFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = DateFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, DateFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, DateFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = DateFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of DateFilter-objects as value to a dart map
|
||||
static Map<String, List<DateFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<DateFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = DateFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
187
mobile/openapi/lib/model/date_filter_nullable.dart
generated
Normal file
187
mobile/openapi/lib/model/date_filter_nullable.dart
generated
Normal file
@@ -0,0 +1,187 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class DateFilterNullable {
|
||||
/// Returns a new [DateFilterNullable] instance.
|
||||
DateFilterNullable({
|
||||
this.eq = const Optional.absent(),
|
||||
this.gt = const Optional.absent(),
|
||||
this.gte = const Optional.absent(),
|
||||
this.lt = const Optional.absent(),
|
||||
this.lte = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
});
|
||||
|
||||
Optional<DateTime?> eq;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> gt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> gte;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> lt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateTime?> lte;
|
||||
|
||||
Optional<DateTime?> ne;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is DateFilterNullable &&
|
||||
other.eq == eq &&
|
||||
other.gt == gt &&
|
||||
other.gte == gte &&
|
||||
other.lt == lt &&
|
||||
other.lte == lte &&
|
||||
other.ne == ne;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(gt == null ? 0 : gt!.hashCode) +
|
||||
(gte == null ? 0 : gte!.hashCode) +
|
||||
(lt == null ? 0 : lt!.hashCode) +
|
||||
(lte == null ? 0 : lte!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'DateFilterNullable[eq=$eq, gt=$gt, gte=$gte, lt=$lt, lte=$lte, ne=$ne]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.gt.isPresent) {
|
||||
final value = this.gt.value;
|
||||
json[r'gt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.gte.isPresent) {
|
||||
final value = this.gte.value;
|
||||
json[r'gte'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.lt.isPresent) {
|
||||
final value = this.lt.value;
|
||||
json[r'lt'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.lte.isPresent) {
|
||||
final value = this.lte.value;
|
||||
json[r'lte'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value == null ? null : (_isEpochMarker(r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [DateFilterNullable] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static DateFilterNullable? fromJson(dynamic value) {
|
||||
upgradeDto(value, "DateFilterNullable");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return DateFilterNullable(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapDateTime(json, r'eq', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
gt: json.containsKey(r'gt') ? Optional.present(mapDateTime(json, r'gt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
gte: json.containsKey(r'gte') ? Optional.present(mapDateTime(json, r'gte', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
lt: json.containsKey(r'lt') ? Optional.present(mapDateTime(json, r'lt', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
lte: json.containsKey(r'lte') ? Optional.present(mapDateTime(json, r'lte', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapDateTime(json, r'ne', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<DateFilterNullable> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <DateFilterNullable>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = DateFilterNullable.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, DateFilterNullable> mapFromJson(dynamic json) {
|
||||
final map = <String, DateFilterNullable>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = DateFilterNullable.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of DateFilterNullable-objects as value to a dart map
|
||||
static Map<String, List<DateFilterNullable>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<DateFilterNullable>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = DateFilterNullable.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
143
mobile/openapi/lib/model/enum_filter_asset_type.dart
generated
Normal file
143
mobile/openapi/lib/model/enum_filter_asset_type.dart
generated
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class EnumFilterAssetType {
|
||||
/// Returns a new [EnumFilterAssetType] instance.
|
||||
EnumFilterAssetType({
|
||||
this.eq = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<AssetTypeEnum?> eq;
|
||||
|
||||
Optional<List<AssetTypeEnum>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<AssetTypeEnum?> ne;
|
||||
|
||||
Optional<List<AssetTypeEnum>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is EnumFilterAssetType &&
|
||||
other.eq == eq &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'EnumFilterAssetType[eq=$eq, in_=$in_, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [EnumFilterAssetType] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static EnumFilterAssetType? fromJson(dynamic value) {
|
||||
upgradeDto(value, "EnumFilterAssetType");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return EnumFilterAssetType(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(AssetTypeEnum.fromJson(json[r'eq'])) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(AssetTypeEnum.listFromJson(json[r'in'])) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(AssetTypeEnum.fromJson(json[r'ne'])) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(AssetTypeEnum.listFromJson(json[r'notIn'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<EnumFilterAssetType> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <EnumFilterAssetType>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = EnumFilterAssetType.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, EnumFilterAssetType> mapFromJson(dynamic json) {
|
||||
final map = <String, EnumFilterAssetType>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = EnumFilterAssetType.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of EnumFilterAssetType-objects as value to a dart map
|
||||
static Map<String, List<EnumFilterAssetType>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<EnumFilterAssetType>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = EnumFilterAssetType.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
143
mobile/openapi/lib/model/enum_filter_asset_visibility.dart
generated
Normal file
143
mobile/openapi/lib/model/enum_filter_asset_visibility.dart
generated
Normal file
@@ -0,0 +1,143 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class EnumFilterAssetVisibility {
|
||||
/// Returns a new [EnumFilterAssetVisibility] instance.
|
||||
EnumFilterAssetVisibility({
|
||||
this.eq = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<AssetVisibility?> eq;
|
||||
|
||||
Optional<List<AssetVisibility>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<AssetVisibility?> ne;
|
||||
|
||||
Optional<List<AssetVisibility>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is EnumFilterAssetVisibility &&
|
||||
other.eq == eq &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'EnumFilterAssetVisibility[eq=$eq, in_=$in_, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [EnumFilterAssetVisibility] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static EnumFilterAssetVisibility? fromJson(dynamic value) {
|
||||
upgradeDto(value, "EnumFilterAssetVisibility");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return EnumFilterAssetVisibility(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(AssetVisibility.fromJson(json[r'eq'])) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(AssetVisibility.listFromJson(json[r'in'])) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(AssetVisibility.fromJson(json[r'ne'])) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(AssetVisibility.listFromJson(json[r'notIn'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<EnumFilterAssetVisibility> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <EnumFilterAssetVisibility>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = EnumFilterAssetVisibility.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, EnumFilterAssetVisibility> mapFromJson(dynamic json) {
|
||||
final map = <String, EnumFilterAssetVisibility>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = EnumFilterAssetVisibility.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of EnumFilterAssetVisibility-objects as value to a dart map
|
||||
static Map<String, List<EnumFilterAssetVisibility>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<EnumFilterAssetVisibility>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = EnumFilterAssetVisibility.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
123
mobile/openapi/lib/model/id_filter.dart
generated
Normal file
123
mobile/openapi/lib/model/id_filter.dart
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class IdFilter {
|
||||
/// Returns a new [IdFilter] instance.
|
||||
IdFilter({
|
||||
this.eq = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> eq;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> ne;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is IdFilter &&
|
||||
other.eq == eq &&
|
||||
other.ne == ne;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'IdFilter[eq=$eq, ne=$ne]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [IdFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static IdFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "IdFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return IdFilter(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapValueOfType<String>(json, r'eq')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapValueOfType<String>(json, r'ne')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<IdFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <IdFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = IdFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, IdFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, IdFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = IdFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of IdFilter-objects as value to a dart map
|
||||
static Map<String, List<IdFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<IdFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = IdFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
111
mobile/openapi/lib/model/id_filter_nullable.dart
generated
Normal file
111
mobile/openapi/lib/model/id_filter_nullable.dart
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class IdFilterNullable {
|
||||
/// Returns a new [IdFilterNullable] instance.
|
||||
IdFilterNullable({
|
||||
this.eq = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
});
|
||||
|
||||
Optional<String?> eq;
|
||||
|
||||
Optional<String?> ne;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is IdFilterNullable &&
|
||||
other.eq == eq &&
|
||||
other.ne == ne;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'IdFilterNullable[eq=$eq, ne=$ne]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [IdFilterNullable] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static IdFilterNullable? fromJson(dynamic value) {
|
||||
upgradeDto(value, "IdFilterNullable");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return IdFilterNullable(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapValueOfType<String>(json, r'eq')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapValueOfType<String>(json, r'ne')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<IdFilterNullable> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <IdFilterNullable>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = IdFilterNullable.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, IdFilterNullable> mapFromJson(dynamic json) {
|
||||
final map = <String, IdFilterNullable>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = IdFilterNullable.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of IdFilterNullable-objects as value to a dart map
|
||||
static Map<String, List<IdFilterNullable>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<IdFilterNullable>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = IdFilterNullable.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
127
mobile/openapi/lib/model/ids_filter.dart
generated
Normal file
127
mobile/openapi/lib/model/ids_filter.dart
generated
Normal file
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class IdsFilter {
|
||||
/// Returns a new [IdsFilter] instance.
|
||||
IdsFilter({
|
||||
this.all = const Optional.present(const []),
|
||||
this.any = const Optional.present(const []),
|
||||
this.none = const Optional.present(const []),
|
||||
});
|
||||
|
||||
Optional<List<String>?> all;
|
||||
|
||||
Optional<List<String>?> any;
|
||||
|
||||
Optional<List<String>?> none;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is IdsFilter &&
|
||||
_deepEquality.equals(other.all, all) &&
|
||||
_deepEquality.equals(other.any, any) &&
|
||||
_deepEquality.equals(other.none, none);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(all.hashCode) +
|
||||
(any.hashCode) +
|
||||
(none.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'IdsFilter[all=$all, any=$any, none=$none]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.all.isPresent) {
|
||||
final value = this.all.value;
|
||||
json[r'all'] = value;
|
||||
}
|
||||
if (this.any.isPresent) {
|
||||
final value = this.any.value;
|
||||
json[r'any'] = value;
|
||||
}
|
||||
if (this.none.isPresent) {
|
||||
final value = this.none.value;
|
||||
json[r'none'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [IdsFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static IdsFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "IdsFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return IdsFilter(
|
||||
all: json.containsKey(r'all') ? Optional.present(json[r'all'] is Iterable
|
||||
? (json[r'all'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
any: json.containsKey(r'any') ? Optional.present(json[r'any'] is Iterable
|
||||
? (json[r'any'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
none: json.containsKey(r'none') ? Optional.present(json[r'none'] is Iterable
|
||||
? (json[r'none'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<IdsFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <IdsFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = IdsFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, IdsFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, IdsFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = IdsFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of IdsFilter-objects as value to a dart map
|
||||
static Map<String, List<IdsFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<IdsFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = IdsFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
51
mobile/openapi/lib/model/metadata_search_dto.dart
generated
51
mobile/openapi/lib/model/metadata_search_dto.dart
generated
@@ -19,8 +19,10 @@ class MetadataSearchDto {
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAfter = const Optional.absent(),
|
||||
this.createdBefore = const Optional.absent(),
|
||||
this.cursor = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.encodedVideoPath = const Optional.absent(),
|
||||
this.filter = const Optional.absent(),
|
||||
this.id = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
@@ -33,6 +35,7 @@ class MetadataSearchDto {
|
||||
this.model = const Optional.absent(),
|
||||
this.ocr = const Optional.absent(),
|
||||
this.order = const Optional.absent(),
|
||||
this.orderBy = const Optional.absent(),
|
||||
this.originalFileName = const Optional.absent(),
|
||||
this.originalPath = const Optional.absent(),
|
||||
this.page = const Optional.absent(),
|
||||
@@ -93,6 +96,15 @@ class MetadataSearchDto {
|
||||
///
|
||||
Optional<DateTime?> createdBefore;
|
||||
|
||||
/// Cursor for the next page of results
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> cursor;
|
||||
|
||||
/// Filter by description text
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -111,6 +123,14 @@ class MetadataSearchDto {
|
||||
///
|
||||
Optional<String?> encodedVideoPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchFilter?> filter;
|
||||
|
||||
/// Filter by asset ID
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -194,6 +214,14 @@ class MetadataSearchDto {
|
||||
///
|
||||
Optional<AssetOrder?> order;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchOrder?> orderBy;
|
||||
|
||||
/// Filter by original file name
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -383,8 +411,10 @@ class MetadataSearchDto {
|
||||
other.country == country &&
|
||||
other.createdAfter == createdAfter &&
|
||||
other.createdBefore == createdBefore &&
|
||||
other.cursor == cursor &&
|
||||
other.description == description &&
|
||||
other.encodedVideoPath == encodedVideoPath &&
|
||||
other.filter == filter &&
|
||||
other.id == id &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
@@ -397,6 +427,7 @@ class MetadataSearchDto {
|
||||
other.model == model &&
|
||||
other.ocr == ocr &&
|
||||
other.order == order &&
|
||||
other.orderBy == orderBy &&
|
||||
other.originalFileName == originalFileName &&
|
||||
other.originalPath == originalPath &&
|
||||
other.page == page &&
|
||||
@@ -429,8 +460,10 @@ class MetadataSearchDto {
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAfter == null ? 0 : createdAfter!.hashCode) +
|
||||
(createdBefore == null ? 0 : createdBefore!.hashCode) +
|
||||
(cursor == null ? 0 : cursor!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
|
||||
(filter == null ? 0 : filter!.hashCode) +
|
||||
(id == null ? 0 : id!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
@@ -443,6 +476,7 @@ class MetadataSearchDto {
|
||||
(model == null ? 0 : model!.hashCode) +
|
||||
(ocr == null ? 0 : ocr!.hashCode) +
|
||||
(order == null ? 0 : order!.hashCode) +
|
||||
(orderBy == null ? 0 : orderBy!.hashCode) +
|
||||
(originalFileName == null ? 0 : originalFileName!.hashCode) +
|
||||
(originalPath == null ? 0 : originalPath!.hashCode) +
|
||||
(page == null ? 0 : page!.hashCode) +
|
||||
@@ -467,7 +501,7 @@ class MetadataSearchDto {
|
||||
(withStacked == null ? 0 : withStacked!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'MetadataSearchDto[albumIds=$albumIds, checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, encodedVideoPath=$encodedVideoPath, id=$id, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
String toString() => 'MetadataSearchDto[albumIds=$albumIds, checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, description=$description, encodedVideoPath=$encodedVideoPath, filter=$filter, id=$id, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, order=$order, orderBy=$orderBy, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -499,6 +533,10 @@ class MetadataSearchDto {
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.cursor.isPresent) {
|
||||
final value = this.cursor.value;
|
||||
json[r'cursor'] = value;
|
||||
}
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
@@ -507,6 +545,10 @@ class MetadataSearchDto {
|
||||
final value = this.encodedVideoPath.value;
|
||||
json[r'encodedVideoPath'] = value;
|
||||
}
|
||||
if (this.filter.isPresent) {
|
||||
final value = this.filter.value;
|
||||
json[r'filter'] = value;
|
||||
}
|
||||
if (this.id.isPresent) {
|
||||
final value = this.id.value;
|
||||
json[r'id'] = value;
|
||||
@@ -555,6 +597,10 @@ class MetadataSearchDto {
|
||||
final value = this.order.value;
|
||||
json[r'order'] = value;
|
||||
}
|
||||
if (this.orderBy.isPresent) {
|
||||
final value = this.orderBy.value;
|
||||
json[r'orderBy'] = value;
|
||||
}
|
||||
if (this.originalFileName.isPresent) {
|
||||
final value = this.originalFileName.value;
|
||||
json[r'originalFileName'] = value;
|
||||
@@ -675,8 +721,10 @@ class MetadataSearchDto {
|
||||
country: json.containsKey(r'country') ? Optional.present(mapValueOfType<String>(json, r'country')) : const Optional.absent(),
|
||||
createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
cursor: json.containsKey(r'cursor') ? Optional.present(mapValueOfType<String>(json, r'cursor')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
encodedVideoPath: json.containsKey(r'encodedVideoPath') ? Optional.present(mapValueOfType<String>(json, r'encodedVideoPath')) : const Optional.absent(),
|
||||
filter: json.containsKey(r'filter') ? Optional.present(SearchFilter.fromJson(json[r'filter'])) : const Optional.absent(),
|
||||
id: json.containsKey(r'id') ? Optional.present(mapValueOfType<String>(json, r'id')) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType<bool>(json, r'isEncoded')) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
@@ -689,6 +737,7 @@ class MetadataSearchDto {
|
||||
model: json.containsKey(r'model') ? Optional.present(mapValueOfType<String>(json, r'model')) : const Optional.absent(),
|
||||
ocr: json.containsKey(r'ocr') ? Optional.present(mapValueOfType<String>(json, r'ocr')) : const Optional.absent(),
|
||||
order: json.containsKey(r'order') ? Optional.present(AssetOrder.fromJson(json[r'order'])) : const Optional.absent(),
|
||||
orderBy: json.containsKey(r'orderBy') ? Optional.present(SearchOrder.fromJson(json[r'orderBy'])) : const Optional.absent(),
|
||||
originalFileName: json.containsKey(r'originalFileName') ? Optional.present(mapValueOfType<String>(json, r'originalFileName')) : const Optional.absent(),
|
||||
originalPath: json.containsKey(r'originalPath') ? Optional.present(mapValueOfType<String>(json, r'originalPath')) : const Optional.absent(),
|
||||
page: json.containsKey(r'page') ? Optional.present(json[r'page'] == null ? null : int.parse('${json[r'page']}')) : const Optional.absent(),
|
||||
|
||||
211
mobile/openapi/lib/model/number_filter.dart
generated
Normal file
211
mobile/openapi/lib/model/number_filter.dart
generated
Normal file
@@ -0,0 +1,211 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class NumberFilter {
|
||||
/// Returns a new [NumberFilter] instance.
|
||||
NumberFilter({
|
||||
this.eq = const Optional.absent(),
|
||||
this.gt = const Optional.absent(),
|
||||
this.gte = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.lt = const Optional.absent(),
|
||||
this.lte = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> eq;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> gt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> gte;
|
||||
|
||||
Optional<List<num>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> lt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> lte;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> ne;
|
||||
|
||||
Optional<List<num>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is NumberFilter &&
|
||||
other.eq == eq &&
|
||||
other.gt == gt &&
|
||||
other.gte == gte &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.lt == lt &&
|
||||
other.lte == lte &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(gt == null ? 0 : gt!.hashCode) +
|
||||
(gte == null ? 0 : gte!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(lt == null ? 0 : lt!.hashCode) +
|
||||
(lte == null ? 0 : lte!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'NumberFilter[eq=$eq, gt=$gt, gte=$gte, in_=$in_, lt=$lt, lte=$lte, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.gt.isPresent) {
|
||||
final value = this.gt.value;
|
||||
json[r'gt'] = value;
|
||||
}
|
||||
if (this.gte.isPresent) {
|
||||
final value = this.gte.value;
|
||||
json[r'gte'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.lt.isPresent) {
|
||||
final value = this.lt.value;
|
||||
json[r'lt'] = value;
|
||||
}
|
||||
if (this.lte.isPresent) {
|
||||
final value = this.lte.value;
|
||||
json[r'lte'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [NumberFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static NumberFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "NumberFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return NumberFilter(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(json[r'eq'] == null ? null : num.parse('${json[r'eq']}')) : const Optional.absent(),
|
||||
gt: json.containsKey(r'gt') ? Optional.present(json[r'gt'] == null ? null : num.parse('${json[r'gt']}')) : const Optional.absent(),
|
||||
gte: json.containsKey(r'gte') ? Optional.present(json[r'gte'] == null ? null : num.parse('${json[r'gte']}')) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(json[r'in'] is Iterable
|
||||
? (json[r'in'] as Iterable).cast<num>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
lt: json.containsKey(r'lt') ? Optional.present(json[r'lt'] == null ? null : num.parse('${json[r'lt']}')) : const Optional.absent(),
|
||||
lte: json.containsKey(r'lte') ? Optional.present(json[r'lte'] == null ? null : num.parse('${json[r'lte']}')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(json[r'ne'] == null ? null : num.parse('${json[r'ne']}')) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(json[r'notIn'] is Iterable
|
||||
? (json[r'notIn'] as Iterable).cast<num>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<NumberFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <NumberFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = NumberFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, NumberFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, NumberFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = NumberFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of NumberFilter-objects as value to a dart map
|
||||
static Map<String, List<NumberFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<NumberFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = NumberFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
199
mobile/openapi/lib/model/number_filter_nullable.dart
generated
Normal file
199
mobile/openapi/lib/model/number_filter_nullable.dart
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class NumberFilterNullable {
|
||||
/// Returns a new [NumberFilterNullable] instance.
|
||||
NumberFilterNullable({
|
||||
this.eq = const Optional.absent(),
|
||||
this.gt = const Optional.absent(),
|
||||
this.gte = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.lt = const Optional.absent(),
|
||||
this.lte = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
Optional<num?> eq;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> gt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> gte;
|
||||
|
||||
Optional<List<num>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> lt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<num?> lte;
|
||||
|
||||
Optional<num?> ne;
|
||||
|
||||
Optional<List<num>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is NumberFilterNullable &&
|
||||
other.eq == eq &&
|
||||
other.gt == gt &&
|
||||
other.gte == gte &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.lt == lt &&
|
||||
other.lte == lte &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(gt == null ? 0 : gt!.hashCode) +
|
||||
(gte == null ? 0 : gte!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(lt == null ? 0 : lt!.hashCode) +
|
||||
(lte == null ? 0 : lte!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'NumberFilterNullable[eq=$eq, gt=$gt, gte=$gte, in_=$in_, lt=$lt, lte=$lte, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.gt.isPresent) {
|
||||
final value = this.gt.value;
|
||||
json[r'gt'] = value;
|
||||
}
|
||||
if (this.gte.isPresent) {
|
||||
final value = this.gte.value;
|
||||
json[r'gte'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.lt.isPresent) {
|
||||
final value = this.lt.value;
|
||||
json[r'lt'] = value;
|
||||
}
|
||||
if (this.lte.isPresent) {
|
||||
final value = this.lte.value;
|
||||
json[r'lte'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [NumberFilterNullable] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static NumberFilterNullable? fromJson(dynamic value) {
|
||||
upgradeDto(value, "NumberFilterNullable");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return NumberFilterNullable(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(json[r'eq'] == null ? null : num.parse('${json[r'eq']}')) : const Optional.absent(),
|
||||
gt: json.containsKey(r'gt') ? Optional.present(json[r'gt'] == null ? null : num.parse('${json[r'gt']}')) : const Optional.absent(),
|
||||
gte: json.containsKey(r'gte') ? Optional.present(json[r'gte'] == null ? null : num.parse('${json[r'gte']}')) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(json[r'in'] is Iterable
|
||||
? (json[r'in'] as Iterable).cast<num>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
lt: json.containsKey(r'lt') ? Optional.present(json[r'lt'] == null ? null : num.parse('${json[r'lt']}')) : const Optional.absent(),
|
||||
lte: json.containsKey(r'lte') ? Optional.present(json[r'lte'] == null ? null : num.parse('${json[r'lte']}')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(json[r'ne'] == null ? null : num.parse('${json[r'ne']}')) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(json[r'notIn'] is Iterable
|
||||
? (json[r'notIn'] as Iterable).cast<num>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<NumberFilterNullable> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <NumberFilterNullable>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = NumberFilterNullable.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, NumberFilterNullable> mapFromJson(dynamic json) {
|
||||
final map = <String, NumberFilterNullable>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = NumberFilterNullable.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of NumberFilterNullable-objects as value to a dart map
|
||||
static Map<String, List<NumberFilterNullable>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<NumberFilterNullable>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = NumberFilterNullable.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
18
mobile/openapi/lib/model/random_search_dto.dart
generated
18
mobile/openapi/lib/model/random_search_dto.dart
generated
@@ -18,6 +18,7 @@ class RandomSearchDto {
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAfter = const Optional.absent(),
|
||||
this.createdBefore = const Optional.absent(),
|
||||
this.filter = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
@@ -74,6 +75,14 @@ class RandomSearchDto {
|
||||
///
|
||||
Optional<DateTime?> createdBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchFilter?> filter;
|
||||
|
||||
/// Filter by encoded status
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -280,6 +289,7 @@ class RandomSearchDto {
|
||||
other.country == country &&
|
||||
other.createdAfter == createdAfter &&
|
||||
other.createdBefore == createdBefore &&
|
||||
other.filter == filter &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
@@ -316,6 +326,7 @@ class RandomSearchDto {
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAfter == null ? 0 : createdAfter!.hashCode) +
|
||||
(createdBefore == null ? 0 : createdBefore!.hashCode) +
|
||||
(filter == null ? 0 : filter!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
@@ -345,7 +356,7 @@ class RandomSearchDto {
|
||||
(withStacked == null ? 0 : withStacked!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'RandomSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, personIds=$personIds, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
String toString() => 'RandomSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, filter=$filter, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, personIds=$personIds, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -373,6 +384,10 @@ class RandomSearchDto {
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.filter.isPresent) {
|
||||
final value = this.filter.value;
|
||||
json[r'filter'] = value;
|
||||
}
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
@@ -512,6 +527,7 @@ class RandomSearchDto {
|
||||
country: json.containsKey(r'country') ? Optional.present(mapValueOfType<String>(json, r'country')) : const Optional.absent(),
|
||||
createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
filter: json.containsKey(r'filter') ? Optional.present(SearchFilter.fromJson(json[r'filter'])) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType<bool>(json, r'isEncoded')) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(mapValueOfType<bool>(json, r'isMotion')) : const Optional.absent(),
|
||||
|
||||
@@ -16,6 +16,7 @@ class SearchAssetResponseDto {
|
||||
required this.count,
|
||||
this.facets = const [],
|
||||
this.items = const [],
|
||||
required this.nextCursor,
|
||||
required this.nextPage,
|
||||
required this.total,
|
||||
});
|
||||
@@ -30,6 +31,9 @@ class SearchAssetResponseDto {
|
||||
|
||||
List<AssetResponseDto> items;
|
||||
|
||||
/// Cursor for the next page of results
|
||||
String? nextCursor;
|
||||
|
||||
/// Next page token
|
||||
String? nextPage;
|
||||
|
||||
@@ -44,6 +48,7 @@ class SearchAssetResponseDto {
|
||||
other.count == count &&
|
||||
_deepEquality.equals(other.facets, facets) &&
|
||||
_deepEquality.equals(other.items, items) &&
|
||||
other.nextCursor == nextCursor &&
|
||||
other.nextPage == nextPage &&
|
||||
other.total == total;
|
||||
|
||||
@@ -53,17 +58,23 @@ class SearchAssetResponseDto {
|
||||
(count.hashCode) +
|
||||
(facets.hashCode) +
|
||||
(items.hashCode) +
|
||||
(nextCursor == null ? 0 : nextCursor!.hashCode) +
|
||||
(nextPage == null ? 0 : nextPage!.hashCode) +
|
||||
(total.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SearchAssetResponseDto[count=$count, facets=$facets, items=$items, nextPage=$nextPage, total=$total]';
|
||||
String toString() => 'SearchAssetResponseDto[count=$count, facets=$facets, items=$items, nextCursor=$nextCursor, nextPage=$nextPage, total=$total]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'count'] = this.count;
|
||||
json[r'facets'] = this.facets;
|
||||
json[r'items'] = this.items;
|
||||
if (this.nextCursor != null) {
|
||||
json[r'nextCursor'] = this.nextCursor;
|
||||
} else {
|
||||
json[r'nextCursor'] = null;
|
||||
}
|
||||
if (this.nextPage != null) {
|
||||
json[r'nextPage'] = this.nextPage;
|
||||
} else {
|
||||
@@ -85,6 +96,7 @@ class SearchAssetResponseDto {
|
||||
count: mapValueOfType<int>(json, r'count')!,
|
||||
facets: SearchFacetResponseDto.listFromJson(json[r'facets']),
|
||||
items: AssetResponseDto.listFromJson(json[r'items']),
|
||||
nextCursor: mapValueOfType<String>(json, r'nextCursor'),
|
||||
nextPage: mapValueOfType<String>(json, r'nextPage'),
|
||||
total: mapValueOfType<int>(json, r'total')!,
|
||||
);
|
||||
@@ -137,6 +149,7 @@ class SearchAssetResponseDto {
|
||||
'count',
|
||||
'facets',
|
||||
'items',
|
||||
'nextCursor',
|
||||
'nextPage',
|
||||
'total',
|
||||
};
|
||||
|
||||
613
mobile/openapi/lib/model/search_filter.dart
generated
Normal file
613
mobile/openapi/lib/model/search_filter.dart
generated
Normal file
@@ -0,0 +1,613 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SearchFilter {
|
||||
/// Returns a new [SearchFilter] instance.
|
||||
SearchFilter({
|
||||
this.albumIds = const Optional.absent(),
|
||||
this.checksum = const Optional.absent(),
|
||||
this.city = const Optional.absent(),
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAt = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.encodedVideoPath = const Optional.absent(),
|
||||
this.fileSizeInBytes = const Optional.absent(),
|
||||
this.hasAlbums = const Optional.absent(),
|
||||
this.hasPeople = const Optional.absent(),
|
||||
this.hasTags = const Optional.absent(),
|
||||
this.id = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
this.isOffline = const Optional.absent(),
|
||||
this.lensModel = const Optional.absent(),
|
||||
this.libraryId = const Optional.absent(),
|
||||
this.make = const Optional.absent(),
|
||||
this.model = const Optional.absent(),
|
||||
this.ocr = const Optional.absent(),
|
||||
this.or = const Optional.present(const []),
|
||||
this.originalFileName = const Optional.absent(),
|
||||
this.originalPath = const Optional.absent(),
|
||||
this.personIds = const Optional.absent(),
|
||||
this.rating = const Optional.absent(),
|
||||
this.state = const Optional.absent(),
|
||||
this.tagIds = const Optional.absent(),
|
||||
this.takenAt = const Optional.absent(),
|
||||
this.trashedAt = const Optional.absent(),
|
||||
this.type = const Optional.absent(),
|
||||
this.updatedAt = const Optional.absent(),
|
||||
this.visibility = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> albumIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilter?> checksum;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> city;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> country;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> createdAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> description;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilter?> encodedVideoPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<NumberFilter?> fileSizeInBytes;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasAlbums;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasPeople;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasTags;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdFilter?> id;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isEncoded;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isFavorite;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isMotion;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isOffline;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> lensModel;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdFilterNullable?> libraryId;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> make;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> model;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringSimilarityFilter?> ocr;
|
||||
|
||||
Optional<List<SearchFilterBranch>?> or;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> originalFileName;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> originalPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> personIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<NumberFilterNullable?> rating;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> state;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> tagIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> takenAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilterNullable?> trashedAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<EnumFilterAssetType?> type;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> updatedAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<EnumFilterAssetVisibility?> visibility;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SearchFilter &&
|
||||
other.albumIds == albumIds &&
|
||||
other.checksum == checksum &&
|
||||
other.city == city &&
|
||||
other.country == country &&
|
||||
other.createdAt == createdAt &&
|
||||
other.description == description &&
|
||||
other.encodedVideoPath == encodedVideoPath &&
|
||||
other.fileSizeInBytes == fileSizeInBytes &&
|
||||
other.hasAlbums == hasAlbums &&
|
||||
other.hasPeople == hasPeople &&
|
||||
other.hasTags == hasTags &&
|
||||
other.id == id &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
other.isOffline == isOffline &&
|
||||
other.lensModel == lensModel &&
|
||||
other.libraryId == libraryId &&
|
||||
other.make == make &&
|
||||
other.model == model &&
|
||||
other.ocr == ocr &&
|
||||
_deepEquality.equals(other.or, or) &&
|
||||
other.originalFileName == originalFileName &&
|
||||
other.originalPath == originalPath &&
|
||||
other.personIds == personIds &&
|
||||
other.rating == rating &&
|
||||
other.state == state &&
|
||||
other.tagIds == tagIds &&
|
||||
other.takenAt == takenAt &&
|
||||
other.trashedAt == trashedAt &&
|
||||
other.type == type &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.visibility == visibility;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(albumIds == null ? 0 : albumIds!.hashCode) +
|
||||
(checksum == null ? 0 : checksum!.hashCode) +
|
||||
(city == null ? 0 : city!.hashCode) +
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAt == null ? 0 : createdAt!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
|
||||
(fileSizeInBytes == null ? 0 : fileSizeInBytes!.hashCode) +
|
||||
(hasAlbums == null ? 0 : hasAlbums!.hashCode) +
|
||||
(hasPeople == null ? 0 : hasPeople!.hashCode) +
|
||||
(hasTags == null ? 0 : hasTags!.hashCode) +
|
||||
(id == null ? 0 : id!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
(isOffline == null ? 0 : isOffline!.hashCode) +
|
||||
(lensModel == null ? 0 : lensModel!.hashCode) +
|
||||
(libraryId == null ? 0 : libraryId!.hashCode) +
|
||||
(make == null ? 0 : make!.hashCode) +
|
||||
(model == null ? 0 : model!.hashCode) +
|
||||
(ocr == null ? 0 : ocr!.hashCode) +
|
||||
(or.hashCode) +
|
||||
(originalFileName == null ? 0 : originalFileName!.hashCode) +
|
||||
(originalPath == null ? 0 : originalPath!.hashCode) +
|
||||
(personIds == null ? 0 : personIds!.hashCode) +
|
||||
(rating == null ? 0 : rating!.hashCode) +
|
||||
(state == null ? 0 : state!.hashCode) +
|
||||
(tagIds == null ? 0 : tagIds!.hashCode) +
|
||||
(takenAt == null ? 0 : takenAt!.hashCode) +
|
||||
(trashedAt == null ? 0 : trashedAt!.hashCode) +
|
||||
(type == null ? 0 : type!.hashCode) +
|
||||
(updatedAt == null ? 0 : updatedAt!.hashCode) +
|
||||
(visibility == null ? 0 : visibility!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SearchFilter[albumIds=$albumIds, checksum=$checksum, city=$city, country=$country, createdAt=$createdAt, description=$description, encodedVideoPath=$encodedVideoPath, fileSizeInBytes=$fileSizeInBytes, hasAlbums=$hasAlbums, hasPeople=$hasPeople, hasTags=$hasTags, id=$id, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, or=$or, originalFileName=$originalFileName, originalPath=$originalPath, personIds=$personIds, rating=$rating, state=$state, tagIds=$tagIds, takenAt=$takenAt, trashedAt=$trashedAt, type=$type, updatedAt=$updatedAt, visibility=$visibility]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.albumIds.isPresent) {
|
||||
final value = this.albumIds.value;
|
||||
json[r'albumIds'] = value;
|
||||
}
|
||||
if (this.checksum.isPresent) {
|
||||
final value = this.checksum.value;
|
||||
json[r'checksum'] = value;
|
||||
}
|
||||
if (this.city.isPresent) {
|
||||
final value = this.city.value;
|
||||
json[r'city'] = value;
|
||||
}
|
||||
if (this.country.isPresent) {
|
||||
final value = this.country.value;
|
||||
json[r'country'] = value;
|
||||
}
|
||||
if (this.createdAt.isPresent) {
|
||||
final value = this.createdAt.value;
|
||||
json[r'createdAt'] = value;
|
||||
}
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.encodedVideoPath.isPresent) {
|
||||
final value = this.encodedVideoPath.value;
|
||||
json[r'encodedVideoPath'] = value;
|
||||
}
|
||||
if (this.fileSizeInBytes.isPresent) {
|
||||
final value = this.fileSizeInBytes.value;
|
||||
json[r'fileSizeInBytes'] = value;
|
||||
}
|
||||
if (this.hasAlbums.isPresent) {
|
||||
final value = this.hasAlbums.value;
|
||||
json[r'hasAlbums'] = value;
|
||||
}
|
||||
if (this.hasPeople.isPresent) {
|
||||
final value = this.hasPeople.value;
|
||||
json[r'hasPeople'] = value;
|
||||
}
|
||||
if (this.hasTags.isPresent) {
|
||||
final value = this.hasTags.value;
|
||||
json[r'hasTags'] = value;
|
||||
}
|
||||
if (this.id.isPresent) {
|
||||
final value = this.id.value;
|
||||
json[r'id'] = value;
|
||||
}
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
}
|
||||
if (this.isFavorite.isPresent) {
|
||||
final value = this.isFavorite.value;
|
||||
json[r'isFavorite'] = value;
|
||||
}
|
||||
if (this.isMotion.isPresent) {
|
||||
final value = this.isMotion.value;
|
||||
json[r'isMotion'] = value;
|
||||
}
|
||||
if (this.isOffline.isPresent) {
|
||||
final value = this.isOffline.value;
|
||||
json[r'isOffline'] = value;
|
||||
}
|
||||
if (this.lensModel.isPresent) {
|
||||
final value = this.lensModel.value;
|
||||
json[r'lensModel'] = value;
|
||||
}
|
||||
if (this.libraryId.isPresent) {
|
||||
final value = this.libraryId.value;
|
||||
json[r'libraryId'] = value;
|
||||
}
|
||||
if (this.make.isPresent) {
|
||||
final value = this.make.value;
|
||||
json[r'make'] = value;
|
||||
}
|
||||
if (this.model.isPresent) {
|
||||
final value = this.model.value;
|
||||
json[r'model'] = value;
|
||||
}
|
||||
if (this.ocr.isPresent) {
|
||||
final value = this.ocr.value;
|
||||
json[r'ocr'] = value;
|
||||
}
|
||||
if (this.or.isPresent) {
|
||||
final value = this.or.value;
|
||||
json[r'or'] = value;
|
||||
}
|
||||
if (this.originalFileName.isPresent) {
|
||||
final value = this.originalFileName.value;
|
||||
json[r'originalFileName'] = value;
|
||||
}
|
||||
if (this.originalPath.isPresent) {
|
||||
final value = this.originalPath.value;
|
||||
json[r'originalPath'] = value;
|
||||
}
|
||||
if (this.personIds.isPresent) {
|
||||
final value = this.personIds.value;
|
||||
json[r'personIds'] = value;
|
||||
}
|
||||
if (this.rating.isPresent) {
|
||||
final value = this.rating.value;
|
||||
json[r'rating'] = value;
|
||||
}
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
if (this.tagIds.isPresent) {
|
||||
final value = this.tagIds.value;
|
||||
json[r'tagIds'] = value;
|
||||
}
|
||||
if (this.takenAt.isPresent) {
|
||||
final value = this.takenAt.value;
|
||||
json[r'takenAt'] = value;
|
||||
}
|
||||
if (this.trashedAt.isPresent) {
|
||||
final value = this.trashedAt.value;
|
||||
json[r'trashedAt'] = value;
|
||||
}
|
||||
if (this.type.isPresent) {
|
||||
final value = this.type.value;
|
||||
json[r'type'] = value;
|
||||
}
|
||||
if (this.updatedAt.isPresent) {
|
||||
final value = this.updatedAt.value;
|
||||
json[r'updatedAt'] = value;
|
||||
}
|
||||
if (this.visibility.isPresent) {
|
||||
final value = this.visibility.value;
|
||||
json[r'visibility'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SearchFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SearchFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SearchFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SearchFilter(
|
||||
albumIds: json.containsKey(r'albumIds') ? Optional.present(IdsFilter.fromJson(json[r'albumIds'])) : const Optional.absent(),
|
||||
checksum: json.containsKey(r'checksum') ? Optional.present(StringFilter.fromJson(json[r'checksum'])) : const Optional.absent(),
|
||||
city: json.containsKey(r'city') ? Optional.present(StringFilterNullable.fromJson(json[r'city'])) : const Optional.absent(),
|
||||
country: json.containsKey(r'country') ? Optional.present(StringFilterNullable.fromJson(json[r'country'])) : const Optional.absent(),
|
||||
createdAt: json.containsKey(r'createdAt') ? Optional.present(DateFilter.fromJson(json[r'createdAt'])) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(StringPatternFilter.fromJson(json[r'description'])) : const Optional.absent(),
|
||||
encodedVideoPath: json.containsKey(r'encodedVideoPath') ? Optional.present(StringFilter.fromJson(json[r'encodedVideoPath'])) : const Optional.absent(),
|
||||
fileSizeInBytes: json.containsKey(r'fileSizeInBytes') ? Optional.present(NumberFilter.fromJson(json[r'fileSizeInBytes'])) : const Optional.absent(),
|
||||
hasAlbums: json.containsKey(r'hasAlbums') ? Optional.present(BoolFilter.fromJson(json[r'hasAlbums'])) : const Optional.absent(),
|
||||
hasPeople: json.containsKey(r'hasPeople') ? Optional.present(BoolFilter.fromJson(json[r'hasPeople'])) : const Optional.absent(),
|
||||
hasTags: json.containsKey(r'hasTags') ? Optional.present(BoolFilter.fromJson(json[r'hasTags'])) : const Optional.absent(),
|
||||
id: json.containsKey(r'id') ? Optional.present(IdFilter.fromJson(json[r'id'])) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(BoolFilter.fromJson(json[r'isEncoded'])) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(BoolFilter.fromJson(json[r'isFavorite'])) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(BoolFilter.fromJson(json[r'isMotion'])) : const Optional.absent(),
|
||||
isOffline: json.containsKey(r'isOffline') ? Optional.present(BoolFilter.fromJson(json[r'isOffline'])) : const Optional.absent(),
|
||||
lensModel: json.containsKey(r'lensModel') ? Optional.present(StringFilterNullable.fromJson(json[r'lensModel'])) : const Optional.absent(),
|
||||
libraryId: json.containsKey(r'libraryId') ? Optional.present(IdFilterNullable.fromJson(json[r'libraryId'])) : const Optional.absent(),
|
||||
make: json.containsKey(r'make') ? Optional.present(StringFilterNullable.fromJson(json[r'make'])) : const Optional.absent(),
|
||||
model: json.containsKey(r'model') ? Optional.present(StringFilterNullable.fromJson(json[r'model'])) : const Optional.absent(),
|
||||
ocr: json.containsKey(r'ocr') ? Optional.present(StringSimilarityFilter.fromJson(json[r'ocr'])) : const Optional.absent(),
|
||||
or: json.containsKey(r'or') ? Optional.present(SearchFilterBranch.listFromJson(json[r'or'])) : const Optional.absent(),
|
||||
originalFileName: json.containsKey(r'originalFileName') ? Optional.present(StringPatternFilter.fromJson(json[r'originalFileName'])) : const Optional.absent(),
|
||||
originalPath: json.containsKey(r'originalPath') ? Optional.present(StringPatternFilter.fromJson(json[r'originalPath'])) : const Optional.absent(),
|
||||
personIds: json.containsKey(r'personIds') ? Optional.present(IdsFilter.fromJson(json[r'personIds'])) : const Optional.absent(),
|
||||
rating: json.containsKey(r'rating') ? Optional.present(NumberFilterNullable.fromJson(json[r'rating'])) : const Optional.absent(),
|
||||
state: json.containsKey(r'state') ? Optional.present(StringFilterNullable.fromJson(json[r'state'])) : const Optional.absent(),
|
||||
tagIds: json.containsKey(r'tagIds') ? Optional.present(IdsFilter.fromJson(json[r'tagIds'])) : const Optional.absent(),
|
||||
takenAt: json.containsKey(r'takenAt') ? Optional.present(DateFilter.fromJson(json[r'takenAt'])) : const Optional.absent(),
|
||||
trashedAt: json.containsKey(r'trashedAt') ? Optional.present(DateFilterNullable.fromJson(json[r'trashedAt'])) : const Optional.absent(),
|
||||
type: json.containsKey(r'type') ? Optional.present(EnumFilterAssetType.fromJson(json[r'type'])) : const Optional.absent(),
|
||||
updatedAt: json.containsKey(r'updatedAt') ? Optional.present(DateFilter.fromJson(json[r'updatedAt'])) : const Optional.absent(),
|
||||
visibility: json.containsKey(r'visibility') ? Optional.present(EnumFilterAssetVisibility.fromJson(json[r'visibility'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SearchFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SearchFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SearchFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SearchFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, SearchFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SearchFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SearchFilter-objects as value to a dart map
|
||||
static Map<String, List<SearchFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SearchFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SearchFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
603
mobile/openapi/lib/model/search_filter_branch.dart
generated
Normal file
603
mobile/openapi/lib/model/search_filter_branch.dart
generated
Normal file
@@ -0,0 +1,603 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SearchFilterBranch {
|
||||
/// Returns a new [SearchFilterBranch] instance.
|
||||
SearchFilterBranch({
|
||||
this.albumIds = const Optional.absent(),
|
||||
this.checksum = const Optional.absent(),
|
||||
this.city = const Optional.absent(),
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAt = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.encodedVideoPath = const Optional.absent(),
|
||||
this.fileSizeInBytes = const Optional.absent(),
|
||||
this.hasAlbums = const Optional.absent(),
|
||||
this.hasPeople = const Optional.absent(),
|
||||
this.hasTags = const Optional.absent(),
|
||||
this.id = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
this.isOffline = const Optional.absent(),
|
||||
this.lensModel = const Optional.absent(),
|
||||
this.libraryId = const Optional.absent(),
|
||||
this.make = const Optional.absent(),
|
||||
this.model = const Optional.absent(),
|
||||
this.ocr = const Optional.absent(),
|
||||
this.originalFileName = const Optional.absent(),
|
||||
this.originalPath = const Optional.absent(),
|
||||
this.personIds = const Optional.absent(),
|
||||
this.rating = const Optional.absent(),
|
||||
this.state = const Optional.absent(),
|
||||
this.tagIds = const Optional.absent(),
|
||||
this.takenAt = const Optional.absent(),
|
||||
this.trashedAt = const Optional.absent(),
|
||||
this.type = const Optional.absent(),
|
||||
this.updatedAt = const Optional.absent(),
|
||||
this.visibility = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> albumIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilter?> checksum;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> city;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> country;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> createdAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> description;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilter?> encodedVideoPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<NumberFilter?> fileSizeInBytes;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasAlbums;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasPeople;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> hasTags;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdFilter?> id;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isEncoded;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isFavorite;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isMotion;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<BoolFilter?> isOffline;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> lensModel;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdFilterNullable?> libraryId;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> make;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> model;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringSimilarityFilter?> ocr;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> originalFileName;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringPatternFilter?> originalPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> personIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<NumberFilterNullable?> rating;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<StringFilterNullable?> state;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<IdsFilter?> tagIds;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> takenAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilterNullable?> trashedAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<EnumFilterAssetType?> type;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<DateFilter?> updatedAt;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<EnumFilterAssetVisibility?> visibility;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SearchFilterBranch &&
|
||||
other.albumIds == albumIds &&
|
||||
other.checksum == checksum &&
|
||||
other.city == city &&
|
||||
other.country == country &&
|
||||
other.createdAt == createdAt &&
|
||||
other.description == description &&
|
||||
other.encodedVideoPath == encodedVideoPath &&
|
||||
other.fileSizeInBytes == fileSizeInBytes &&
|
||||
other.hasAlbums == hasAlbums &&
|
||||
other.hasPeople == hasPeople &&
|
||||
other.hasTags == hasTags &&
|
||||
other.id == id &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
other.isOffline == isOffline &&
|
||||
other.lensModel == lensModel &&
|
||||
other.libraryId == libraryId &&
|
||||
other.make == make &&
|
||||
other.model == model &&
|
||||
other.ocr == ocr &&
|
||||
other.originalFileName == originalFileName &&
|
||||
other.originalPath == originalPath &&
|
||||
other.personIds == personIds &&
|
||||
other.rating == rating &&
|
||||
other.state == state &&
|
||||
other.tagIds == tagIds &&
|
||||
other.takenAt == takenAt &&
|
||||
other.trashedAt == trashedAt &&
|
||||
other.type == type &&
|
||||
other.updatedAt == updatedAt &&
|
||||
other.visibility == visibility;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(albumIds == null ? 0 : albumIds!.hashCode) +
|
||||
(checksum == null ? 0 : checksum!.hashCode) +
|
||||
(city == null ? 0 : city!.hashCode) +
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAt == null ? 0 : createdAt!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
|
||||
(fileSizeInBytes == null ? 0 : fileSizeInBytes!.hashCode) +
|
||||
(hasAlbums == null ? 0 : hasAlbums!.hashCode) +
|
||||
(hasPeople == null ? 0 : hasPeople!.hashCode) +
|
||||
(hasTags == null ? 0 : hasTags!.hashCode) +
|
||||
(id == null ? 0 : id!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
(isOffline == null ? 0 : isOffline!.hashCode) +
|
||||
(lensModel == null ? 0 : lensModel!.hashCode) +
|
||||
(libraryId == null ? 0 : libraryId!.hashCode) +
|
||||
(make == null ? 0 : make!.hashCode) +
|
||||
(model == null ? 0 : model!.hashCode) +
|
||||
(ocr == null ? 0 : ocr!.hashCode) +
|
||||
(originalFileName == null ? 0 : originalFileName!.hashCode) +
|
||||
(originalPath == null ? 0 : originalPath!.hashCode) +
|
||||
(personIds == null ? 0 : personIds!.hashCode) +
|
||||
(rating == null ? 0 : rating!.hashCode) +
|
||||
(state == null ? 0 : state!.hashCode) +
|
||||
(tagIds == null ? 0 : tagIds!.hashCode) +
|
||||
(takenAt == null ? 0 : takenAt!.hashCode) +
|
||||
(trashedAt == null ? 0 : trashedAt!.hashCode) +
|
||||
(type == null ? 0 : type!.hashCode) +
|
||||
(updatedAt == null ? 0 : updatedAt!.hashCode) +
|
||||
(visibility == null ? 0 : visibility!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SearchFilterBranch[albumIds=$albumIds, checksum=$checksum, city=$city, country=$country, createdAt=$createdAt, description=$description, encodedVideoPath=$encodedVideoPath, fileSizeInBytes=$fileSizeInBytes, hasAlbums=$hasAlbums, hasPeople=$hasPeople, hasTags=$hasTags, id=$id, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, originalFileName=$originalFileName, originalPath=$originalPath, personIds=$personIds, rating=$rating, state=$state, tagIds=$tagIds, takenAt=$takenAt, trashedAt=$trashedAt, type=$type, updatedAt=$updatedAt, visibility=$visibility]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.albumIds.isPresent) {
|
||||
final value = this.albumIds.value;
|
||||
json[r'albumIds'] = value;
|
||||
}
|
||||
if (this.checksum.isPresent) {
|
||||
final value = this.checksum.value;
|
||||
json[r'checksum'] = value;
|
||||
}
|
||||
if (this.city.isPresent) {
|
||||
final value = this.city.value;
|
||||
json[r'city'] = value;
|
||||
}
|
||||
if (this.country.isPresent) {
|
||||
final value = this.country.value;
|
||||
json[r'country'] = value;
|
||||
}
|
||||
if (this.createdAt.isPresent) {
|
||||
final value = this.createdAt.value;
|
||||
json[r'createdAt'] = value;
|
||||
}
|
||||
if (this.description.isPresent) {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.encodedVideoPath.isPresent) {
|
||||
final value = this.encodedVideoPath.value;
|
||||
json[r'encodedVideoPath'] = value;
|
||||
}
|
||||
if (this.fileSizeInBytes.isPresent) {
|
||||
final value = this.fileSizeInBytes.value;
|
||||
json[r'fileSizeInBytes'] = value;
|
||||
}
|
||||
if (this.hasAlbums.isPresent) {
|
||||
final value = this.hasAlbums.value;
|
||||
json[r'hasAlbums'] = value;
|
||||
}
|
||||
if (this.hasPeople.isPresent) {
|
||||
final value = this.hasPeople.value;
|
||||
json[r'hasPeople'] = value;
|
||||
}
|
||||
if (this.hasTags.isPresent) {
|
||||
final value = this.hasTags.value;
|
||||
json[r'hasTags'] = value;
|
||||
}
|
||||
if (this.id.isPresent) {
|
||||
final value = this.id.value;
|
||||
json[r'id'] = value;
|
||||
}
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
}
|
||||
if (this.isFavorite.isPresent) {
|
||||
final value = this.isFavorite.value;
|
||||
json[r'isFavorite'] = value;
|
||||
}
|
||||
if (this.isMotion.isPresent) {
|
||||
final value = this.isMotion.value;
|
||||
json[r'isMotion'] = value;
|
||||
}
|
||||
if (this.isOffline.isPresent) {
|
||||
final value = this.isOffline.value;
|
||||
json[r'isOffline'] = value;
|
||||
}
|
||||
if (this.lensModel.isPresent) {
|
||||
final value = this.lensModel.value;
|
||||
json[r'lensModel'] = value;
|
||||
}
|
||||
if (this.libraryId.isPresent) {
|
||||
final value = this.libraryId.value;
|
||||
json[r'libraryId'] = value;
|
||||
}
|
||||
if (this.make.isPresent) {
|
||||
final value = this.make.value;
|
||||
json[r'make'] = value;
|
||||
}
|
||||
if (this.model.isPresent) {
|
||||
final value = this.model.value;
|
||||
json[r'model'] = value;
|
||||
}
|
||||
if (this.ocr.isPresent) {
|
||||
final value = this.ocr.value;
|
||||
json[r'ocr'] = value;
|
||||
}
|
||||
if (this.originalFileName.isPresent) {
|
||||
final value = this.originalFileName.value;
|
||||
json[r'originalFileName'] = value;
|
||||
}
|
||||
if (this.originalPath.isPresent) {
|
||||
final value = this.originalPath.value;
|
||||
json[r'originalPath'] = value;
|
||||
}
|
||||
if (this.personIds.isPresent) {
|
||||
final value = this.personIds.value;
|
||||
json[r'personIds'] = value;
|
||||
}
|
||||
if (this.rating.isPresent) {
|
||||
final value = this.rating.value;
|
||||
json[r'rating'] = value;
|
||||
}
|
||||
if (this.state.isPresent) {
|
||||
final value = this.state.value;
|
||||
json[r'state'] = value;
|
||||
}
|
||||
if (this.tagIds.isPresent) {
|
||||
final value = this.tagIds.value;
|
||||
json[r'tagIds'] = value;
|
||||
}
|
||||
if (this.takenAt.isPresent) {
|
||||
final value = this.takenAt.value;
|
||||
json[r'takenAt'] = value;
|
||||
}
|
||||
if (this.trashedAt.isPresent) {
|
||||
final value = this.trashedAt.value;
|
||||
json[r'trashedAt'] = value;
|
||||
}
|
||||
if (this.type.isPresent) {
|
||||
final value = this.type.value;
|
||||
json[r'type'] = value;
|
||||
}
|
||||
if (this.updatedAt.isPresent) {
|
||||
final value = this.updatedAt.value;
|
||||
json[r'updatedAt'] = value;
|
||||
}
|
||||
if (this.visibility.isPresent) {
|
||||
final value = this.visibility.value;
|
||||
json[r'visibility'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SearchFilterBranch] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SearchFilterBranch? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SearchFilterBranch");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SearchFilterBranch(
|
||||
albumIds: json.containsKey(r'albumIds') ? Optional.present(IdsFilter.fromJson(json[r'albumIds'])) : const Optional.absent(),
|
||||
checksum: json.containsKey(r'checksum') ? Optional.present(StringFilter.fromJson(json[r'checksum'])) : const Optional.absent(),
|
||||
city: json.containsKey(r'city') ? Optional.present(StringFilterNullable.fromJson(json[r'city'])) : const Optional.absent(),
|
||||
country: json.containsKey(r'country') ? Optional.present(StringFilterNullable.fromJson(json[r'country'])) : const Optional.absent(),
|
||||
createdAt: json.containsKey(r'createdAt') ? Optional.present(DateFilter.fromJson(json[r'createdAt'])) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(StringPatternFilter.fromJson(json[r'description'])) : const Optional.absent(),
|
||||
encodedVideoPath: json.containsKey(r'encodedVideoPath') ? Optional.present(StringFilter.fromJson(json[r'encodedVideoPath'])) : const Optional.absent(),
|
||||
fileSizeInBytes: json.containsKey(r'fileSizeInBytes') ? Optional.present(NumberFilter.fromJson(json[r'fileSizeInBytes'])) : const Optional.absent(),
|
||||
hasAlbums: json.containsKey(r'hasAlbums') ? Optional.present(BoolFilter.fromJson(json[r'hasAlbums'])) : const Optional.absent(),
|
||||
hasPeople: json.containsKey(r'hasPeople') ? Optional.present(BoolFilter.fromJson(json[r'hasPeople'])) : const Optional.absent(),
|
||||
hasTags: json.containsKey(r'hasTags') ? Optional.present(BoolFilter.fromJson(json[r'hasTags'])) : const Optional.absent(),
|
||||
id: json.containsKey(r'id') ? Optional.present(IdFilter.fromJson(json[r'id'])) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(BoolFilter.fromJson(json[r'isEncoded'])) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(BoolFilter.fromJson(json[r'isFavorite'])) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(BoolFilter.fromJson(json[r'isMotion'])) : const Optional.absent(),
|
||||
isOffline: json.containsKey(r'isOffline') ? Optional.present(BoolFilter.fromJson(json[r'isOffline'])) : const Optional.absent(),
|
||||
lensModel: json.containsKey(r'lensModel') ? Optional.present(StringFilterNullable.fromJson(json[r'lensModel'])) : const Optional.absent(),
|
||||
libraryId: json.containsKey(r'libraryId') ? Optional.present(IdFilterNullable.fromJson(json[r'libraryId'])) : const Optional.absent(),
|
||||
make: json.containsKey(r'make') ? Optional.present(StringFilterNullable.fromJson(json[r'make'])) : const Optional.absent(),
|
||||
model: json.containsKey(r'model') ? Optional.present(StringFilterNullable.fromJson(json[r'model'])) : const Optional.absent(),
|
||||
ocr: json.containsKey(r'ocr') ? Optional.present(StringSimilarityFilter.fromJson(json[r'ocr'])) : const Optional.absent(),
|
||||
originalFileName: json.containsKey(r'originalFileName') ? Optional.present(StringPatternFilter.fromJson(json[r'originalFileName'])) : const Optional.absent(),
|
||||
originalPath: json.containsKey(r'originalPath') ? Optional.present(StringPatternFilter.fromJson(json[r'originalPath'])) : const Optional.absent(),
|
||||
personIds: json.containsKey(r'personIds') ? Optional.present(IdsFilter.fromJson(json[r'personIds'])) : const Optional.absent(),
|
||||
rating: json.containsKey(r'rating') ? Optional.present(NumberFilterNullable.fromJson(json[r'rating'])) : const Optional.absent(),
|
||||
state: json.containsKey(r'state') ? Optional.present(StringFilterNullable.fromJson(json[r'state'])) : const Optional.absent(),
|
||||
tagIds: json.containsKey(r'tagIds') ? Optional.present(IdsFilter.fromJson(json[r'tagIds'])) : const Optional.absent(),
|
||||
takenAt: json.containsKey(r'takenAt') ? Optional.present(DateFilter.fromJson(json[r'takenAt'])) : const Optional.absent(),
|
||||
trashedAt: json.containsKey(r'trashedAt') ? Optional.present(DateFilterNullable.fromJson(json[r'trashedAt'])) : const Optional.absent(),
|
||||
type: json.containsKey(r'type') ? Optional.present(EnumFilterAssetType.fromJson(json[r'type'])) : const Optional.absent(),
|
||||
updatedAt: json.containsKey(r'updatedAt') ? Optional.present(DateFilter.fromJson(json[r'updatedAt'])) : const Optional.absent(),
|
||||
visibility: json.containsKey(r'visibility') ? Optional.present(EnumFilterAssetVisibility.fromJson(json[r'visibility'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SearchFilterBranch> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SearchFilterBranch>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SearchFilterBranch.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SearchFilterBranch> mapFromJson(dynamic json) {
|
||||
final map = <String, SearchFilterBranch>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SearchFilterBranch.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SearchFilterBranch-objects as value to a dart map
|
||||
static Map<String, List<SearchFilterBranch>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SearchFilterBranch>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SearchFilterBranch.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
123
mobile/openapi/lib/model/search_order.dart
generated
Normal file
123
mobile/openapi/lib/model/search_order.dart
generated
Normal file
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class SearchOrder {
|
||||
/// Returns a new [SearchOrder] instance.
|
||||
SearchOrder({
|
||||
this.direction = const Optional.absent(),
|
||||
this.field = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<AssetOrder?> direction;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchOrderField?> field;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SearchOrder &&
|
||||
other.direction == direction &&
|
||||
other.field == field;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(direction == null ? 0 : direction!.hashCode) +
|
||||
(field == null ? 0 : field!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SearchOrder[direction=$direction, field=$field]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.direction.isPresent) {
|
||||
final value = this.direction.value;
|
||||
json[r'direction'] = value;
|
||||
}
|
||||
if (this.field.isPresent) {
|
||||
final value = this.field.value;
|
||||
json[r'field'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SearchOrder] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SearchOrder? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SearchOrder");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SearchOrder(
|
||||
direction: json.containsKey(r'direction') ? Optional.present(AssetOrder.fromJson(json[r'direction'])) : const Optional.absent(),
|
||||
field: json.containsKey(r'field') ? Optional.present(SearchOrderField.fromJson(json[r'field'])) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SearchOrder> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SearchOrder>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SearchOrder.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SearchOrder> mapFromJson(dynamic json) {
|
||||
final map = <String, SearchOrder>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SearchOrder.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SearchOrder-objects as value to a dart map
|
||||
static Map<String, List<SearchOrder>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SearchOrder>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SearchOrder.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
94
mobile/openapi/lib/model/search_order_field.dart
generated
Normal file
94
mobile/openapi/lib/model/search_order_field.dart
generated
Normal file
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
|
||||
enum SearchOrderField {
|
||||
fileCreatedAt._(r'fileCreatedAt'),
|
||||
localDateTime._(r'localDateTime'),
|
||||
fileSizeInBytes._(r'fileSizeInBytes'),
|
||||
rating._(r'rating'),
|
||||
;
|
||||
|
||||
/// Instantiate a new enum with the provided value.
|
||||
const SearchOrderField._(this._value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String _value;
|
||||
|
||||
@override
|
||||
String toString() => _value;
|
||||
|
||||
/// Encodes this enum as a value suitable for JSON.
|
||||
String toJson() => _value;
|
||||
|
||||
/// Returns the instance of [SearchOrderField] that was successfully decoded
|
||||
/// from the passed [value] on success, null otherwise.
|
||||
static SearchOrderField? fromJson(dynamic value) => SearchOrderFieldTypeTransformer().decode(value);
|
||||
|
||||
/// Returns a [List] containing instances of [SearchOrderField]
|
||||
/// that were successfully decoded from the passed [JSON][json].
|
||||
static List<SearchOrderField> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SearchOrderField>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SearchOrderField.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [SearchOrderField] to String,
|
||||
/// and [decode] dynamic data back to [SearchOrderField].
|
||||
class SearchOrderFieldTypeTransformer {
|
||||
factory SearchOrderFieldTypeTransformer() => _instance ??= const SearchOrderFieldTypeTransformer._();
|
||||
|
||||
const SearchOrderFieldTypeTransformer._();
|
||||
|
||||
/// Encodes this enum as a value suitable for JSON.
|
||||
String encode(SearchOrderField data) => data._value;
|
||||
|
||||
/// Returns the instance of [SearchOrderField] that was successfully decoded
|
||||
/// from the passed [data] value on success, null otherwise.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
SearchOrderField? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data is SearchOrderField) {
|
||||
return data;
|
||||
}
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'fileCreatedAt': return SearchOrderField.fileCreatedAt;
|
||||
case r'localDateTime': return SearchOrderField.localDateTime;
|
||||
case r'fileSizeInBytes': return SearchOrderField.fileSizeInBytes;
|
||||
case r'rating': return SearchOrderField.rating;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// The singleton instance of this transformer.
|
||||
static SearchOrderFieldTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
35
mobile/openapi/lib/model/smart_search_dto.dart
generated
35
mobile/openapi/lib/model/smart_search_dto.dart
generated
@@ -18,6 +18,8 @@ class SmartSearchDto {
|
||||
this.country = const Optional.absent(),
|
||||
this.createdAfter = const Optional.absent(),
|
||||
this.createdBefore = const Optional.absent(),
|
||||
this.cursor = const Optional.absent(),
|
||||
this.filter = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
@@ -76,6 +78,23 @@ class SmartSearchDto {
|
||||
///
|
||||
Optional<DateTime?> createdBefore;
|
||||
|
||||
/// Cursor for the next page of results
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> cursor;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchFilter?> filter;
|
||||
|
||||
/// Filter by encoded status
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -303,6 +322,8 @@ class SmartSearchDto {
|
||||
other.country == country &&
|
||||
other.createdAfter == createdAfter &&
|
||||
other.createdBefore == createdBefore &&
|
||||
other.cursor == cursor &&
|
||||
other.filter == filter &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
@@ -341,6 +362,8 @@ class SmartSearchDto {
|
||||
(country == null ? 0 : country!.hashCode) +
|
||||
(createdAfter == null ? 0 : createdAfter!.hashCode) +
|
||||
(createdBefore == null ? 0 : createdBefore!.hashCode) +
|
||||
(cursor == null ? 0 : cursor!.hashCode) +
|
||||
(filter == null ? 0 : filter!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
@@ -372,7 +395,7 @@ class SmartSearchDto {
|
||||
(withExif == null ? 0 : withExif!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, page=$page, personIds=$personIds, query=$query, queryAssetId=$queryAssetId, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]';
|
||||
String toString() => 'SmartSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, cursor=$cursor, filter=$filter, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, language=$language, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, page=$page, personIds=$personIds, query=$query, queryAssetId=$queryAssetId, rating=$rating, size=$size, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility, withDeleted=$withDeleted, withExif=$withExif]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -400,6 +423,14 @@ class SmartSearchDto {
|
||||
? value.millisecondsSinceEpoch
|
||||
: value.toUtc().toIso8601String());
|
||||
}
|
||||
if (this.cursor.isPresent) {
|
||||
final value = this.cursor.value;
|
||||
json[r'cursor'] = value;
|
||||
}
|
||||
if (this.filter.isPresent) {
|
||||
final value = this.filter.value;
|
||||
json[r'filter'] = value;
|
||||
}
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
@@ -547,6 +578,8 @@ class SmartSearchDto {
|
||||
country: json.containsKey(r'country') ? Optional.present(mapValueOfType<String>(json, r'country')) : const Optional.absent(),
|
||||
createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
cursor: json.containsKey(r'cursor') ? Optional.present(mapValueOfType<String>(json, r'cursor')) : const Optional.absent(),
|
||||
filter: json.containsKey(r'filter') ? Optional.present(SearchFilter.fromJson(json[r'filter'])) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType<bool>(json, r'isEncoded')) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(mapValueOfType<bool>(json, r'isMotion')) : const Optional.absent(),
|
||||
|
||||
18
mobile/openapi/lib/model/statistics_search_dto.dart
generated
18
mobile/openapi/lib/model/statistics_search_dto.dart
generated
@@ -19,6 +19,7 @@ class StatisticsSearchDto {
|
||||
this.createdAfter = const Optional.absent(),
|
||||
this.createdBefore = const Optional.absent(),
|
||||
this.description = const Optional.absent(),
|
||||
this.filter = const Optional.absent(),
|
||||
this.isEncoded = const Optional.absent(),
|
||||
this.isFavorite = const Optional.absent(),
|
||||
this.isMotion = const Optional.absent(),
|
||||
@@ -79,6 +80,14 @@ class StatisticsSearchDto {
|
||||
///
|
||||
Optional<String?> description;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<SearchFilter?> filter;
|
||||
|
||||
/// Filter by encoded status
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -238,6 +247,7 @@ class StatisticsSearchDto {
|
||||
other.createdAfter == createdAfter &&
|
||||
other.createdBefore == createdBefore &&
|
||||
other.description == description &&
|
||||
other.filter == filter &&
|
||||
other.isEncoded == isEncoded &&
|
||||
other.isFavorite == isFavorite &&
|
||||
other.isMotion == isMotion &&
|
||||
@@ -270,6 +280,7 @@ class StatisticsSearchDto {
|
||||
(createdAfter == null ? 0 : createdAfter!.hashCode) +
|
||||
(createdBefore == null ? 0 : createdBefore!.hashCode) +
|
||||
(description == null ? 0 : description!.hashCode) +
|
||||
(filter == null ? 0 : filter!.hashCode) +
|
||||
(isEncoded == null ? 0 : isEncoded!.hashCode) +
|
||||
(isFavorite == null ? 0 : isFavorite!.hashCode) +
|
||||
(isMotion == null ? 0 : isMotion!.hashCode) +
|
||||
@@ -294,7 +305,7 @@ class StatisticsSearchDto {
|
||||
(visibility == null ? 0 : visibility!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StatisticsSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, personIds=$personIds, rating=$rating, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility]';
|
||||
String toString() => 'StatisticsSearchDto[albumIds=$albumIds, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, description=$description, filter=$filter, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, ocr=$ocr, personIds=$personIds, rating=$rating, state=$state, tagIds=$tagIds, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, visibility=$visibility]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -326,6 +337,10 @@ class StatisticsSearchDto {
|
||||
final value = this.description.value;
|
||||
json[r'description'] = value;
|
||||
}
|
||||
if (this.filter.isPresent) {
|
||||
final value = this.filter.value;
|
||||
json[r'filter'] = value;
|
||||
}
|
||||
if (this.isEncoded.isPresent) {
|
||||
final value = this.isEncoded.value;
|
||||
json[r'isEncoded'] = value;
|
||||
@@ -446,6 +461,7 @@ class StatisticsSearchDto {
|
||||
createdAfter: json.containsKey(r'createdAfter') ? Optional.present(mapDateTime(json, r'createdAfter', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
createdBefore: json.containsKey(r'createdBefore') ? Optional.present(mapDateTime(json, r'createdBefore', r'/^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$/')) : const Optional.absent(),
|
||||
description: json.containsKey(r'description') ? Optional.present(mapValueOfType<String>(json, r'description')) : const Optional.absent(),
|
||||
filter: json.containsKey(r'filter') ? Optional.present(SearchFilter.fromJson(json[r'filter'])) : const Optional.absent(),
|
||||
isEncoded: json.containsKey(r'isEncoded') ? Optional.present(mapValueOfType<bool>(json, r'isEncoded')) : const Optional.absent(),
|
||||
isFavorite: json.containsKey(r'isFavorite') ? Optional.present(mapValueOfType<bool>(json, r'isFavorite')) : const Optional.absent(),
|
||||
isMotion: json.containsKey(r'isMotion') ? Optional.present(mapValueOfType<bool>(json, r'isMotion')) : const Optional.absent(),
|
||||
|
||||
147
mobile/openapi/lib/model/string_filter.dart
generated
Normal file
147
mobile/openapi/lib/model/string_filter.dart
generated
Normal file
@@ -0,0 +1,147 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class StringFilter {
|
||||
/// Returns a new [StringFilter] instance.
|
||||
StringFilter({
|
||||
this.eq = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> eq;
|
||||
|
||||
Optional<List<String>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> ne;
|
||||
|
||||
Optional<List<String>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is StringFilter &&
|
||||
other.eq == eq &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StringFilter[eq=$eq, in_=$in_, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [StringFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static StringFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "StringFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return StringFilter(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapValueOfType<String>(json, r'eq')) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(json[r'in'] is Iterable
|
||||
? (json[r'in'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapValueOfType<String>(json, r'ne')) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(json[r'notIn'] is Iterable
|
||||
? (json[r'notIn'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<StringFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StringFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StringFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, StringFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, StringFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = StringFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of StringFilter-objects as value to a dart map
|
||||
static Map<String, List<StringFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<StringFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = StringFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
135
mobile/openapi/lib/model/string_filter_nullable.dart
generated
Normal file
135
mobile/openapi/lib/model/string_filter_nullable.dart
generated
Normal file
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class StringFilterNullable {
|
||||
/// Returns a new [StringFilterNullable] instance.
|
||||
StringFilterNullable({
|
||||
this.eq = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
});
|
||||
|
||||
Optional<String?> eq;
|
||||
|
||||
Optional<List<String>?> in_;
|
||||
|
||||
Optional<String?> ne;
|
||||
|
||||
Optional<List<String>?> notIn;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is StringFilterNullable &&
|
||||
other.eq == eq &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn);
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StringFilterNullable[eq=$eq, in_=$in_, ne=$ne, notIn=$notIn]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [StringFilterNullable] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static StringFilterNullable? fromJson(dynamic value) {
|
||||
upgradeDto(value, "StringFilterNullable");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return StringFilterNullable(
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapValueOfType<String>(json, r'eq')) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(json[r'in'] is Iterable
|
||||
? (json[r'in'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapValueOfType<String>(json, r'ne')) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(json[r'notIn'] is Iterable
|
||||
? (json[r'notIn'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<StringFilterNullable> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StringFilterNullable>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StringFilterNullable.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, StringFilterNullable> mapFromJson(dynamic json) {
|
||||
final map = <String, StringFilterNullable>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = StringFilterNullable.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of StringFilterNullable-objects as value to a dart map
|
||||
static Map<String, List<StringFilterNullable>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<StringFilterNullable>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = StringFilterNullable.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
199
mobile/openapi/lib/model/string_pattern_filter.dart
generated
Normal file
199
mobile/openapi/lib/model/string_pattern_filter.dart
generated
Normal file
@@ -0,0 +1,199 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class StringPatternFilter {
|
||||
/// Returns a new [StringPatternFilter] instance.
|
||||
StringPatternFilter({
|
||||
this.endsWith = const Optional.absent(),
|
||||
this.eq = const Optional.absent(),
|
||||
this.in_ = const Optional.present(const []),
|
||||
this.like = const Optional.absent(),
|
||||
this.ne = const Optional.absent(),
|
||||
this.notIn = const Optional.present(const []),
|
||||
this.notLike = const Optional.absent(),
|
||||
this.startsWith = const Optional.absent(),
|
||||
});
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> endsWith;
|
||||
|
||||
Optional<String?> eq;
|
||||
|
||||
Optional<List<String>?> in_;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> like;
|
||||
|
||||
Optional<String?> ne;
|
||||
|
||||
Optional<List<String>?> notIn;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> notLike;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
Optional<String?> startsWith;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is StringPatternFilter &&
|
||||
other.endsWith == endsWith &&
|
||||
other.eq == eq &&
|
||||
_deepEquality.equals(other.in_, in_) &&
|
||||
other.like == like &&
|
||||
other.ne == ne &&
|
||||
_deepEquality.equals(other.notIn, notIn) &&
|
||||
other.notLike == notLike &&
|
||||
other.startsWith == startsWith;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(endsWith == null ? 0 : endsWith!.hashCode) +
|
||||
(eq == null ? 0 : eq!.hashCode) +
|
||||
(in_.hashCode) +
|
||||
(like == null ? 0 : like!.hashCode) +
|
||||
(ne == null ? 0 : ne!.hashCode) +
|
||||
(notIn.hashCode) +
|
||||
(notLike == null ? 0 : notLike!.hashCode) +
|
||||
(startsWith == null ? 0 : startsWith!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StringPatternFilter[endsWith=$endsWith, eq=$eq, in_=$in_, like=$like, ne=$ne, notIn=$notIn, notLike=$notLike, startsWith=$startsWith]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
if (this.endsWith.isPresent) {
|
||||
final value = this.endsWith.value;
|
||||
json[r'endsWith'] = value;
|
||||
}
|
||||
if (this.eq.isPresent) {
|
||||
final value = this.eq.value;
|
||||
json[r'eq'] = value;
|
||||
}
|
||||
if (this.in_.isPresent) {
|
||||
final value = this.in_.value;
|
||||
json[r'in'] = value;
|
||||
}
|
||||
if (this.like.isPresent) {
|
||||
final value = this.like.value;
|
||||
json[r'like'] = value;
|
||||
}
|
||||
if (this.ne.isPresent) {
|
||||
final value = this.ne.value;
|
||||
json[r'ne'] = value;
|
||||
}
|
||||
if (this.notIn.isPresent) {
|
||||
final value = this.notIn.value;
|
||||
json[r'notIn'] = value;
|
||||
}
|
||||
if (this.notLike.isPresent) {
|
||||
final value = this.notLike.value;
|
||||
json[r'notLike'] = value;
|
||||
}
|
||||
if (this.startsWith.isPresent) {
|
||||
final value = this.startsWith.value;
|
||||
json[r'startsWith'] = value;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [StringPatternFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static StringPatternFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "StringPatternFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return StringPatternFilter(
|
||||
endsWith: json.containsKey(r'endsWith') ? Optional.present(mapValueOfType<String>(json, r'endsWith')) : const Optional.absent(),
|
||||
eq: json.containsKey(r'eq') ? Optional.present(mapValueOfType<String>(json, r'eq')) : const Optional.absent(),
|
||||
in_: json.containsKey(r'in') ? Optional.present(json[r'in'] is Iterable
|
||||
? (json[r'in'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
like: json.containsKey(r'like') ? Optional.present(mapValueOfType<String>(json, r'like')) : const Optional.absent(),
|
||||
ne: json.containsKey(r'ne') ? Optional.present(mapValueOfType<String>(json, r'ne')) : const Optional.absent(),
|
||||
notIn: json.containsKey(r'notIn') ? Optional.present(json[r'notIn'] is Iterable
|
||||
? (json[r'notIn'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const []) : const Optional.absent(),
|
||||
notLike: json.containsKey(r'notLike') ? Optional.present(mapValueOfType<String>(json, r'notLike')) : const Optional.absent(),
|
||||
startsWith: json.containsKey(r'startsWith') ? Optional.present(mapValueOfType<String>(json, r'startsWith')) : const Optional.absent(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<StringPatternFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StringPatternFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StringPatternFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, StringPatternFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, StringPatternFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = StringPatternFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of StringPatternFilter-objects as value to a dart map
|
||||
static Map<String, List<StringPatternFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<StringPatternFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = StringPatternFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
};
|
||||
}
|
||||
|
||||
99
mobile/openapi/lib/model/string_similarity_filter.dart
generated
Normal file
99
mobile/openapi/lib/model/string_similarity_filter.dart
generated
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.18
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
class StringSimilarityFilter {
|
||||
/// Returns a new [StringSimilarityFilter] instance.
|
||||
StringSimilarityFilter({
|
||||
required this.matches,
|
||||
});
|
||||
|
||||
String matches;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is StringSimilarityFilter &&
|
||||
other.matches == matches;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(matches.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'StringSimilarityFilter[matches=$matches]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'matches'] = this.matches;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [StringSimilarityFilter] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static StringSimilarityFilter? fromJson(dynamic value) {
|
||||
upgradeDto(value, "StringSimilarityFilter");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return StringSimilarityFilter(
|
||||
matches: mapValueOfType<String>(json, r'matches')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<StringSimilarityFilter> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <StringSimilarityFilter>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = StringSimilarityFilter.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, StringSimilarityFilter> mapFromJson(dynamic json) {
|
||||
final map = <String, StringSimilarityFilter>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = StringSimilarityFilter.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of StringSimilarityFilter-objects as value to a dart map
|
||||
static Map<String, List<StringSimilarityFilter>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<StringSimilarityFilter>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = StringSimilarityFilter.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'matches',
|
||||
};
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1617,6 +1617,168 @@ export type SearchExploreResponseDto = {
|
||||
fieldName: string;
|
||||
items: SearchExploreItem[];
|
||||
};
|
||||
export type IdsFilter = {
|
||||
all?: string[];
|
||||
"any"?: string[];
|
||||
none?: string[];
|
||||
};
|
||||
export type StringFilter = {
|
||||
eq?: string;
|
||||
"in"?: string[];
|
||||
ne?: string;
|
||||
notIn?: string[];
|
||||
};
|
||||
export type StringFilterNullable = {
|
||||
eq?: string | null;
|
||||
"in"?: string[];
|
||||
ne?: string | null;
|
||||
notIn?: string[];
|
||||
};
|
||||
export type DateFilter = {
|
||||
eq?: string;
|
||||
gt?: string;
|
||||
gte?: string;
|
||||
lt?: string;
|
||||
lte?: string;
|
||||
ne?: string;
|
||||
};
|
||||
export type StringPatternFilter = {
|
||||
endsWith?: string;
|
||||
eq?: string | null;
|
||||
"in"?: string[];
|
||||
like?: string;
|
||||
ne?: string | null;
|
||||
notIn?: string[];
|
||||
notLike?: string;
|
||||
startsWith?: string;
|
||||
};
|
||||
export type NumberFilter = {
|
||||
eq?: number;
|
||||
gt?: number;
|
||||
gte?: number;
|
||||
"in"?: number[];
|
||||
lt?: number;
|
||||
lte?: number;
|
||||
ne?: number;
|
||||
notIn?: number[];
|
||||
};
|
||||
export type BoolFilter = {
|
||||
eq: boolean;
|
||||
};
|
||||
export type IdFilter = {
|
||||
eq?: string;
|
||||
ne?: string;
|
||||
};
|
||||
export type IdFilterNullable = {
|
||||
eq?: string | null;
|
||||
ne?: string | null;
|
||||
};
|
||||
export type StringSimilarityFilter = {
|
||||
matches: string;
|
||||
};
|
||||
export type NumberFilterNullable = {
|
||||
eq?: number | null;
|
||||
gt?: number;
|
||||
gte?: number;
|
||||
"in"?: number[];
|
||||
lt?: number;
|
||||
lte?: number;
|
||||
ne?: number | null;
|
||||
notIn?: number[];
|
||||
};
|
||||
export type DateFilterNullable = {
|
||||
eq?: string | null;
|
||||
gt?: string;
|
||||
gte?: string;
|
||||
lt?: string;
|
||||
lte?: string;
|
||||
ne?: string | null;
|
||||
};
|
||||
export type EnumFilterAssetType = {
|
||||
eq?: AssetTypeEnum;
|
||||
"in"?: AssetTypeEnum[];
|
||||
ne?: AssetTypeEnum;
|
||||
notIn?: AssetTypeEnum[];
|
||||
};
|
||||
export type EnumFilterAssetVisibility = {
|
||||
eq?: AssetVisibility;
|
||||
"in"?: AssetVisibility[];
|
||||
ne?: AssetVisibility;
|
||||
notIn?: AssetVisibility[];
|
||||
};
|
||||
export type SearchFilterBranch = {
|
||||
albumIds?: IdsFilter;
|
||||
checksum?: StringFilter;
|
||||
city?: StringFilterNullable;
|
||||
country?: StringFilterNullable;
|
||||
createdAt?: DateFilter;
|
||||
description?: StringPatternFilter;
|
||||
encodedVideoPath?: StringFilter;
|
||||
fileSizeInBytes?: NumberFilter;
|
||||
hasAlbums?: BoolFilter;
|
||||
hasPeople?: BoolFilter;
|
||||
hasTags?: BoolFilter;
|
||||
id?: IdFilter;
|
||||
isEncoded?: BoolFilter;
|
||||
isFavorite?: BoolFilter;
|
||||
isMotion?: BoolFilter;
|
||||
isOffline?: BoolFilter;
|
||||
lensModel?: StringFilterNullable;
|
||||
libraryId?: IdFilterNullable;
|
||||
make?: StringFilterNullable;
|
||||
model?: StringFilterNullable;
|
||||
ocr?: StringSimilarityFilter;
|
||||
originalFileName?: StringPatternFilter;
|
||||
originalPath?: StringPatternFilter;
|
||||
personIds?: IdsFilter;
|
||||
rating?: NumberFilterNullable;
|
||||
state?: StringFilterNullable;
|
||||
tagIds?: IdsFilter;
|
||||
takenAt?: DateFilter;
|
||||
trashedAt?: DateFilterNullable;
|
||||
"type"?: EnumFilterAssetType;
|
||||
updatedAt?: DateFilter;
|
||||
visibility?: EnumFilterAssetVisibility;
|
||||
};
|
||||
export type SearchFilter = {
|
||||
albumIds?: IdsFilter;
|
||||
checksum?: StringFilter;
|
||||
city?: StringFilterNullable;
|
||||
country?: StringFilterNullable;
|
||||
createdAt?: DateFilter;
|
||||
description?: StringPatternFilter;
|
||||
encodedVideoPath?: StringFilter;
|
||||
fileSizeInBytes?: NumberFilter;
|
||||
hasAlbums?: BoolFilter;
|
||||
hasPeople?: BoolFilter;
|
||||
hasTags?: BoolFilter;
|
||||
id?: IdFilter;
|
||||
isEncoded?: BoolFilter;
|
||||
isFavorite?: BoolFilter;
|
||||
isMotion?: BoolFilter;
|
||||
isOffline?: BoolFilter;
|
||||
lensModel?: StringFilterNullable;
|
||||
libraryId?: IdFilterNullable;
|
||||
make?: StringFilterNullable;
|
||||
model?: StringFilterNullable;
|
||||
ocr?: StringSimilarityFilter;
|
||||
or?: SearchFilterBranch[];
|
||||
originalFileName?: StringPatternFilter;
|
||||
originalPath?: StringPatternFilter;
|
||||
personIds?: IdsFilter;
|
||||
rating?: NumberFilterNullable;
|
||||
state?: StringFilterNullable;
|
||||
tagIds?: IdsFilter;
|
||||
takenAt?: DateFilter;
|
||||
trashedAt?: DateFilterNullable;
|
||||
"type"?: EnumFilterAssetType;
|
||||
updatedAt?: DateFilter;
|
||||
visibility?: EnumFilterAssetVisibility;
|
||||
};
|
||||
export type SearchOrder = {
|
||||
direction?: AssetOrder;
|
||||
field?: SearchOrderField;
|
||||
};
|
||||
export type MetadataSearchDto = {
|
||||
/** Filter by album IDs */
|
||||
albumIds?: string[];
|
||||
@@ -1630,10 +1792,13 @@ export type MetadataSearchDto = {
|
||||
createdAfter?: string;
|
||||
/** Filter by creation date (before) */
|
||||
createdBefore?: string;
|
||||
/** Cursor for the next page of results */
|
||||
cursor?: string;
|
||||
/** Filter by description text */
|
||||
description?: string;
|
||||
/** Filter by encoded video file path */
|
||||
encodedVideoPath?: string;
|
||||
filter?: SearchFilter;
|
||||
/** Filter by asset ID */
|
||||
id?: string;
|
||||
/** Filter by encoded status */
|
||||
@@ -1658,6 +1823,7 @@ export type MetadataSearchDto = {
|
||||
ocr?: string;
|
||||
/** Sort order */
|
||||
order?: AssetOrder;
|
||||
orderBy?: SearchOrder;
|
||||
/** Filter by original file name */
|
||||
originalFileName?: string;
|
||||
/** Filter by original file path */
|
||||
@@ -1725,6 +1891,8 @@ export type SearchAssetResponseDto = {
|
||||
count: number;
|
||||
facets: SearchFacetResponseDto[];
|
||||
items: AssetResponseDto[];
|
||||
/** Cursor for the next page of results */
|
||||
nextCursor: string | null;
|
||||
/** Next page token */
|
||||
nextPage: string | null;
|
||||
/** Total number of matching assets */
|
||||
@@ -1757,6 +1925,7 @@ export type RandomSearchDto = {
|
||||
createdAfter?: string;
|
||||
/** Filter by creation date (before) */
|
||||
createdBefore?: string;
|
||||
filter?: SearchFilter;
|
||||
/** Filter by encoded status */
|
||||
isEncoded?: boolean;
|
||||
/** Filter by favorite status */
|
||||
@@ -1821,6 +1990,9 @@ export type SmartSearchDto = {
|
||||
createdAfter?: string;
|
||||
/** Filter by creation date (before) */
|
||||
createdBefore?: string;
|
||||
/** Cursor for the next page of results */
|
||||
cursor?: string;
|
||||
filter?: SearchFilter;
|
||||
/** Filter by encoded status */
|
||||
isEncoded?: boolean;
|
||||
/** Filter by favorite status */
|
||||
@@ -1891,6 +2063,7 @@ export type StatisticsSearchDto = {
|
||||
createdBefore?: string;
|
||||
/** Filter by description text */
|
||||
description?: string;
|
||||
filter?: SearchFilter;
|
||||
/** Filter by encoded status */
|
||||
isEncoded?: boolean;
|
||||
/** Filter by favorite status */
|
||||
@@ -7501,6 +7674,12 @@ export enum JobName {
|
||||
IntegrityDeleteReportType = "IntegrityDeleteReportType",
|
||||
IntegrityDeleteReports = "IntegrityDeleteReports"
|
||||
}
|
||||
export enum SearchOrderField {
|
||||
FileCreatedAt = "fileCreatedAt",
|
||||
LocalDateTime = "localDateTime",
|
||||
FileSizeInBytes = "fileSizeInBytes",
|
||||
Rating = "rating"
|
||||
}
|
||||
export enum SearchSuggestionType {
|
||||
Country = "country",
|
||||
State = "state",
|
||||
|
||||
@@ -120,6 +120,16 @@ describe(SearchController.name, () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject a deprecated field combined with a new structure field', async () => {
|
||||
const { status, body } = await request(ctx.getHttpServer())
|
||||
.post('/search/metadata')
|
||||
.send({ filter: {}, city: 'Oslo' });
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(
|
||||
errorDto.validationError([{ path: ['city'], message: 'Deprecated field city cannot be combined with filter' }]),
|
||||
);
|
||||
});
|
||||
|
||||
describe('POST /search/random', () => {
|
||||
it('should be an authenticated route', async () => {
|
||||
await request(ctx.getHttpServer()).post('/search/random');
|
||||
|
||||
@@ -69,7 +69,11 @@ export class SearchController {
|
||||
@Endpoint({
|
||||
summary: 'Search large assets',
|
||||
description: 'Search for assets that are considered large based on specified criteria.',
|
||||
history: new HistoryBuilder().added('v1').beta('v1').stable('v2'),
|
||||
history: new HistoryBuilder()
|
||||
.added('v1')
|
||||
.beta('v1')
|
||||
.stable('v2')
|
||||
.deprecated('v3.1.0', { replacementId: 'searchAssets' }),
|
||||
})
|
||||
searchLargeAssets(@Auth() auth: AuthDto, @Query() dto: LargeAssetSearchDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.searchLargeAssets(auth, dto);
|
||||
|
||||
@@ -14,32 +14,40 @@ import {
|
||||
import { isoDatetimeToDate, nonEmptyPartial, stringToBool } from 'src/validation';
|
||||
import z from 'zod';
|
||||
|
||||
const ADDED_V3_1 = new HistoryBuilder().added('v3.1.0').getExtensions();
|
||||
|
||||
// fields deprecated in favor of the structured filter tree
|
||||
const DEPRECATED_FLAT_FIELD = {
|
||||
...new HistoryBuilder().added('v1').stable('v2').deprecated('v3.1.0').getExtensions(),
|
||||
deprecated: true,
|
||||
};
|
||||
|
||||
const BaseSearchSchema = z.object({
|
||||
libraryId: z.uuidv4().nullish().describe('Library ID to filter by'),
|
||||
type: AssetTypeSchema.optional(),
|
||||
isEncoded: z.boolean().optional().describe('Filter by encoded status'),
|
||||
isFavorite: z.boolean().optional().describe('Filter by favorite status'),
|
||||
isMotion: z.boolean().optional().describe('Filter by motion photo status'),
|
||||
isOffline: z.boolean().optional().describe('Filter by offline status'),
|
||||
visibility: AssetVisibilitySchema.optional(),
|
||||
createdBefore: isoDatetimeToDate.optional().describe('Filter by creation date (before)'),
|
||||
createdAfter: isoDatetimeToDate.optional().describe('Filter by creation date (after)'),
|
||||
updatedBefore: isoDatetimeToDate.optional().describe('Filter by update date (before)'),
|
||||
updatedAfter: isoDatetimeToDate.optional().describe('Filter by update date (after)'),
|
||||
trashedBefore: isoDatetimeToDate.optional().describe('Filter by trash date (before)'),
|
||||
trashedAfter: isoDatetimeToDate.optional().describe('Filter by trash date (after)'),
|
||||
takenBefore: isoDatetimeToDate.optional().describe('Filter by taken date (before)'),
|
||||
takenAfter: isoDatetimeToDate.optional().describe('Filter by taken date (after)'),
|
||||
city: z.string().nullable().optional().describe('Filter by city name'),
|
||||
state: z.string().nullable().optional().describe('Filter by state/province name'),
|
||||
country: z.string().nullable().optional().describe('Filter by country name'),
|
||||
make: z.string().nullable().optional().describe('Filter by camera make'),
|
||||
model: z.string().nullable().optional().describe('Filter by camera model'),
|
||||
lensModel: z.string().nullable().optional().describe('Filter by lens model'),
|
||||
isNotInAlbum: z.boolean().optional().describe('Filter assets not in any album'),
|
||||
personIds: z.array(z.uuidv4()).optional().describe('Filter by person IDs'),
|
||||
tagIds: z.array(z.uuidv4()).nullish().describe('Filter by tag IDs'),
|
||||
albumIds: z.array(z.uuidv4()).optional().describe('Filter by album IDs'),
|
||||
libraryId: z.uuidv4().nullish().describe('Library ID to filter by').meta(DEPRECATED_FLAT_FIELD),
|
||||
type: AssetTypeSchema.optional().meta(DEPRECATED_FLAT_FIELD),
|
||||
isEncoded: z.boolean().optional().describe('Filter by encoded status').meta(DEPRECATED_FLAT_FIELD),
|
||||
isFavorite: z.boolean().optional().describe('Filter by favorite status').meta(DEPRECATED_FLAT_FIELD),
|
||||
isMotion: z.boolean().optional().describe('Filter by motion photo status').meta(DEPRECATED_FLAT_FIELD),
|
||||
isOffline: z.boolean().optional().describe('Filter by offline status').meta(DEPRECATED_FLAT_FIELD),
|
||||
visibility: AssetVisibilitySchema.optional().meta(DEPRECATED_FLAT_FIELD),
|
||||
createdBefore: isoDatetimeToDate.optional().describe('Filter by creation date (before)').meta(DEPRECATED_FLAT_FIELD),
|
||||
createdAfter: isoDatetimeToDate.optional().describe('Filter by creation date (after)').meta(DEPRECATED_FLAT_FIELD),
|
||||
updatedBefore: isoDatetimeToDate.optional().describe('Filter by update date (before)').meta(DEPRECATED_FLAT_FIELD),
|
||||
updatedAfter: isoDatetimeToDate.optional().describe('Filter by update date (after)').meta(DEPRECATED_FLAT_FIELD),
|
||||
trashedBefore: isoDatetimeToDate.optional().describe('Filter by trash date (before)').meta(DEPRECATED_FLAT_FIELD),
|
||||
trashedAfter: isoDatetimeToDate.optional().describe('Filter by trash date (after)').meta(DEPRECATED_FLAT_FIELD),
|
||||
takenBefore: isoDatetimeToDate.optional().describe('Filter by taken date (before)').meta(DEPRECATED_FLAT_FIELD),
|
||||
takenAfter: isoDatetimeToDate.optional().describe('Filter by taken date (after)').meta(DEPRECATED_FLAT_FIELD),
|
||||
city: z.string().nullable().optional().describe('Filter by city name').meta(DEPRECATED_FLAT_FIELD),
|
||||
state: z.string().nullable().optional().describe('Filter by state/province name').meta(DEPRECATED_FLAT_FIELD),
|
||||
country: z.string().nullable().optional().describe('Filter by country name').meta(DEPRECATED_FLAT_FIELD),
|
||||
make: z.string().nullable().optional().describe('Filter by camera make').meta(DEPRECATED_FLAT_FIELD),
|
||||
model: z.string().nullable().optional().describe('Filter by camera model').meta(DEPRECATED_FLAT_FIELD),
|
||||
lensModel: z.string().nullable().optional().describe('Filter by lens model').meta(DEPRECATED_FLAT_FIELD),
|
||||
isNotInAlbum: z.boolean().optional().describe('Filter assets not in any album').meta(DEPRECATED_FLAT_FIELD),
|
||||
personIds: z.array(z.uuidv4()).optional().describe('Filter by person IDs').meta(DEPRECATED_FLAT_FIELD),
|
||||
tagIds: z.array(z.uuidv4()).nullish().describe('Filter by tag IDs').meta(DEPRECATED_FLAT_FIELD),
|
||||
albumIds: z.array(z.uuidv4()).optional().describe('Filter by album IDs').meta(DEPRECATED_FLAT_FIELD),
|
||||
rating: z
|
||||
.int()
|
||||
.min(1)
|
||||
@@ -52,51 +60,24 @@ const BaseSearchSchema = z.object({
|
||||
.stable('v2')
|
||||
.updated('v2.6.0', 'Using -1 as a rating is deprecated and will be removed in the next major version.')
|
||||
.updated('v3', 'Using -1 as a rating is no longer valid.')
|
||||
.deprecated('v3.1.0')
|
||||
.getExtensions(),
|
||||
deprecated: true,
|
||||
}),
|
||||
ocr: z.string().optional().describe('Filter by OCR text content'),
|
||||
ocr: z.string().optional().describe('Filter by OCR text content').meta(DEPRECATED_FLAT_FIELD),
|
||||
});
|
||||
|
||||
const BaseSearchWithResultsSchema = BaseSearchSchema.extend({
|
||||
withDeleted: z.boolean().optional().describe('Include deleted assets'),
|
||||
withDeleted: z.boolean().optional().describe('Include deleted assets').meta(DEPRECATED_FLAT_FIELD),
|
||||
withExif: z.boolean().optional().describe('Include EXIF data in response'),
|
||||
size: z.int().min(1).max(1000).optional().describe('Number of results to return'),
|
||||
size: z.int().min(1).max(1000).default(250).describe('Number of results to return'),
|
||||
});
|
||||
|
||||
const RandomSearchSchema = BaseSearchWithResultsSchema.extend({
|
||||
withStacked: z.boolean().optional().describe('Include stacked assets'),
|
||||
withPeople: z.boolean().optional().describe('Include people data in response'),
|
||||
}).meta({ id: 'RandomSearchDto' });
|
||||
|
||||
const LargeAssetSearchSchema = BaseSearchWithResultsSchema.extend({
|
||||
minFileSize: z.coerce.number().int().min(0).optional().describe('Minimum file size in bytes'),
|
||||
size: z.coerce.number().int().min(1).max(1000).optional().describe('Number of results to return'),
|
||||
size: z.coerce.number().int().min(1).max(1000).default(250).describe('Number of results to return'),
|
||||
}).meta({ id: 'LargeAssetSearchDto' });
|
||||
|
||||
const MetadataSearchSchema = RandomSearchSchema.extend({
|
||||
id: z.uuidv4().optional().describe('Filter by asset ID'),
|
||||
description: z.string().trim().optional().describe('Filter by description text'),
|
||||
checksum: z.string().optional().describe('Filter by file checksum'),
|
||||
originalFileName: z.string().trim().optional().describe('Filter by original file name'),
|
||||
originalPath: z.string().optional().describe('Filter by original file path'),
|
||||
previewPath: z.string().optional().describe('Filter by preview file path'),
|
||||
thumbnailPath: z.string().optional().describe('Filter by thumbnail file path'),
|
||||
encodedVideoPath: z.string().optional().describe('Filter by encoded video file path'),
|
||||
order: AssetOrderSchema.default(AssetOrder.Desc).optional().describe('Sort order'),
|
||||
page: z.int().min(1).optional().describe('Page number'),
|
||||
}).meta({ id: 'MetadataSearchDto' });
|
||||
|
||||
const StatisticsSearchSchema = BaseSearchSchema.extend({
|
||||
description: z.string().trim().optional().describe('Filter by description text'),
|
||||
}).meta({ id: 'StatisticsSearchDto' });
|
||||
|
||||
const SmartSearchSchema = BaseSearchWithResultsSchema.extend({
|
||||
query: z.string().trim().optional().describe('Natural language search query'),
|
||||
queryAssetId: z.uuidv4().optional().describe('Asset ID to use as search reference'),
|
||||
language: z.string().optional().describe('Search language code'),
|
||||
page: z.int().min(1).optional().describe('Page number'),
|
||||
}).meta({ id: 'SmartSearchDto' });
|
||||
|
||||
const SearchPlacesSchema = z
|
||||
.object({
|
||||
name: z.string().describe('Place name to search for'),
|
||||
@@ -319,6 +300,88 @@ export type SearchOrder = z.infer<typeof SearchOrderSchema>;
|
||||
export type SearchFilter = z.infer<typeof SearchFilterSchema>;
|
||||
export type SearchFilterBranch = z.infer<typeof SearchFilterBranchSchema>;
|
||||
|
||||
const NEW_SHAPE_FIELDS = ['filter', 'orderBy', 'cursor'] as const;
|
||||
|
||||
export const isNewShapeRequest = (dto: Partial<Record<(typeof NEW_SHAPE_FIELDS)[number], unknown>>): boolean =>
|
||||
NEW_SHAPE_FIELDS.some((field) => dto[field] !== undefined);
|
||||
|
||||
/**
|
||||
* The structured shape and the deprecated flat search fields are mutually exclusive
|
||||
* TODO(v4): remove together with the deprecated flat fields.
|
||||
*/
|
||||
const withShapeExclusivity = <T extends z.ZodObject<z.ZodRawShape>>(schema: T) => {
|
||||
const deprecatedFields = Object.keys(schema.shape).filter(
|
||||
(field) => (schema.shape[field] as z.ZodType).meta()?.deprecated,
|
||||
);
|
||||
|
||||
return schema.superRefine((dto, ctx) => {
|
||||
const values = dto as Record<string, unknown>;
|
||||
const newShapeFields = NEW_SHAPE_FIELDS.filter((field) => values[field] !== undefined);
|
||||
if (newShapeFields.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const field of deprecatedFields) {
|
||||
if (values[field] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.addIssue({
|
||||
code: 'custom',
|
||||
path: [field],
|
||||
message: `Deprecated field ${field} cannot be combined with ${newShapeFields.join('/')}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const filterField = SearchFilterSchema.optional().meta(ADDED_V3_1);
|
||||
const cursorField = z.string().min(1).optional().describe('Cursor for the next page of results').meta(ADDED_V3_1);
|
||||
|
||||
const RandomSearchBaseSchema = BaseSearchWithResultsSchema.extend({
|
||||
withStacked: z.boolean().optional().describe('Include stacked assets'),
|
||||
withPeople: z.boolean().optional().describe('Include people data in response'),
|
||||
filter: filterField,
|
||||
});
|
||||
|
||||
const RandomSearchSchema = withShapeExclusivity(RandomSearchBaseSchema).meta({ id: 'RandomSearchDto' });
|
||||
|
||||
const MetadataSearchSchema = withShapeExclusivity(
|
||||
RandomSearchBaseSchema.extend({
|
||||
id: z.uuidv4().optional().describe('Filter by asset ID').meta(DEPRECATED_FLAT_FIELD),
|
||||
description: z.string().trim().optional().describe('Filter by description text').meta(DEPRECATED_FLAT_FIELD),
|
||||
checksum: z.string().optional().describe('Filter by file checksum').meta(DEPRECATED_FLAT_FIELD),
|
||||
originalFileName: z.string().trim().optional().describe('Filter by original file name').meta(DEPRECATED_FLAT_FIELD),
|
||||
originalPath: z.string().optional().describe('Filter by original file path').meta(DEPRECATED_FLAT_FIELD),
|
||||
previewPath: z.string().optional().describe('Filter by preview file path').meta(DEPRECATED_FLAT_FIELD),
|
||||
thumbnailPath: z.string().optional().describe('Filter by thumbnail file path').meta(DEPRECATED_FLAT_FIELD),
|
||||
encodedVideoPath: z.string().optional().describe('Filter by encoded video file path').meta(DEPRECATED_FLAT_FIELD),
|
||||
order: AssetOrderSchema.optional().describe('Sort order').meta(DEPRECATED_FLAT_FIELD),
|
||||
page: z.int().min(1).optional().describe('Page number').meta(DEPRECATED_FLAT_FIELD),
|
||||
orderBy: SearchOrderSchema.optional().meta(ADDED_V3_1),
|
||||
cursor: cursorField,
|
||||
}),
|
||||
).meta({ id: 'MetadataSearchDto' });
|
||||
|
||||
const StatisticsSearchSchema = withShapeExclusivity(
|
||||
BaseSearchSchema.extend({
|
||||
description: z.string().trim().optional().describe('Filter by description text').meta(DEPRECATED_FLAT_FIELD),
|
||||
filter: filterField,
|
||||
}),
|
||||
).meta({ id: 'StatisticsSearchDto' });
|
||||
|
||||
const SmartSearchSchema = withShapeExclusivity(
|
||||
BaseSearchWithResultsSchema.extend({
|
||||
size: z.int().min(1).max(1000).default(100).describe('Number of results to return'),
|
||||
query: z.string().trim().optional().describe('Natural language search query'),
|
||||
queryAssetId: z.uuidv4().optional().describe('Asset ID to use as search reference'),
|
||||
language: z.string().optional().describe('Search language code'),
|
||||
page: z.int().min(1).optional().describe('Page number').meta(DEPRECATED_FLAT_FIELD),
|
||||
filter: filterField,
|
||||
cursor: cursorField,
|
||||
}),
|
||||
).meta({ id: 'SmartSearchDto' });
|
||||
|
||||
export class RandomSearchDto extends createZodDto(RandomSearchSchema) {}
|
||||
export class LargeAssetSearchDto extends createZodDto(LargeAssetSearchSchema) {}
|
||||
export class MetadataSearchDto extends createZodDto(MetadataSearchSchema) {}
|
||||
@@ -372,7 +435,8 @@ const SearchAssetResponseSchema = z
|
||||
count: z.int().min(0).describe('Number of assets in this page'),
|
||||
items: z.array(AssetResponseSchema),
|
||||
facets: z.array(SearchFacetResponseSchema),
|
||||
nextPage: z.string().nullable().describe('Next page token'),
|
||||
nextPage: z.string().nullable().describe('Next page token').meta(DEPRECATED_FLAT_FIELD),
|
||||
nextCursor: z.string().nullable().describe('Cursor for the next page of results').meta(ADDED_V3_1),
|
||||
})
|
||||
.meta({ id: 'SearchAssetResponseDto' });
|
||||
|
||||
|
||||
@@ -449,12 +449,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$2
|
||||
$4
|
||||
offset
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (empty)
|
||||
select
|
||||
@@ -490,12 +496,18 @@ from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
true
|
||||
(
|
||||
"asset"."visibility" != $1
|
||||
or "asset"."ownerId" = $2
|
||||
)
|
||||
and true
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$1
|
||||
$3
|
||||
offset
|
||||
$4
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (or-exif-only)
|
||||
select
|
||||
@@ -532,12 +544,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and "asset_exif"."city" = $2
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and "asset_exif"."city" = $4
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (string-eq-null)
|
||||
select
|
||||
@@ -574,12 +592,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and "asset_exif"."city" is null
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$2
|
||||
$4
|
||||
offset
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (string-pattern-like)
|
||||
select
|
||||
@@ -616,12 +640,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and f_unaccent ("asset_exif"."description") ilike ('%' || f_unaccent ($2) || '%')
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and f_unaccent ("asset_exif"."description") ilike ('%' || f_unaccent ($4) || '%')
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (string-pattern-notLike)
|
||||
select
|
||||
@@ -658,12 +688,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and f_unaccent ("asset_exif"."description") not ilike ('%' || f_unaccent ($2) || '%')
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and f_unaccent ("asset_exif"."description") not ilike ('%' || f_unaccent ($4) || '%')
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (string-pattern-startsWith)
|
||||
select
|
||||
@@ -700,12 +736,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and f_unaccent ("asset"."originalFileName") ilike (f_unaccent ($2) || '%')
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and f_unaccent ("asset"."originalFileName") ilike (f_unaccent ($4) || '%')
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (string-similarity-ocr)
|
||||
select
|
||||
@@ -742,19 +784,25 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
from
|
||||
"ocr_search"
|
||||
where
|
||||
"ocr_search"."assetId" = "asset"."id"
|
||||
and f_unaccent (ocr_search.text) %>> f_unaccent ($2)
|
||||
and f_unaccent (ocr_search.text) %>> f_unaccent ($4)
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (ids-any)
|
||||
select
|
||||
@@ -791,19 +839,25 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
from
|
||||
"album_asset"
|
||||
where
|
||||
"album_asset"."assetId" = "asset"."id"
|
||||
and "album_asset"."albumId" = any ($2::uuid[])
|
||||
and "album_asset"."albumId" = any ($4::uuid[])
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (ids-all)
|
||||
select
|
||||
@@ -840,6 +894,10 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
"asset_face"."assetId"
|
||||
@@ -848,18 +906,20 @@ where
|
||||
where
|
||||
"asset_face"."assetId" = "asset"."id"
|
||||
and "asset_face"."deletedAt" is null
|
||||
and "asset_face"."isVisible" = $2
|
||||
and "asset_face"."personId" = any ($3::uuid[])
|
||||
and "asset_face"."isVisible" = $4
|
||||
and "asset_face"."personId" = any ($5::uuid[])
|
||||
group by
|
||||
"asset_face"."assetId"
|
||||
having
|
||||
count(distinct "asset_face"."personId") = $4
|
||||
count(distinct "asset_face"."personId") = $6
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$5
|
||||
$7
|
||||
offset
|
||||
$8
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (ids-all-single)
|
||||
select
|
||||
@@ -896,19 +956,25 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
from
|
||||
"album_asset"
|
||||
where
|
||||
"album_asset"."assetId" = "asset"."id"
|
||||
and "album_asset"."albumId" = any ($2::uuid[])
|
||||
and "album_asset"."albumId" = any ($4::uuid[])
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (ids-none)
|
||||
select
|
||||
@@ -945,6 +1011,10 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and not exists (
|
||||
select
|
||||
from
|
||||
@@ -952,13 +1022,15 @@ where
|
||||
inner join "tag_closure" on "tag_asset"."tagId" = "tag_closure"."id_descendant"
|
||||
where
|
||||
"tag_asset"."assetId" = "asset"."id"
|
||||
and "tag_closure"."id_ancestor" = any ($2::uuid[])
|
||||
and "tag_closure"."id_ancestor" = any ($4::uuid[])
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (ids-tags-all)
|
||||
select
|
||||
@@ -995,6 +1067,10 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
"tag_asset"."assetId"
|
||||
@@ -1003,17 +1079,19 @@ where
|
||||
inner join "tag_closure" on "tag_asset"."tagId" = "tag_closure"."id_descendant"
|
||||
where
|
||||
"tag_asset"."assetId" = "asset"."id"
|
||||
and "tag_closure"."id_ancestor" = any ($2::uuid[])
|
||||
and "tag_closure"."id_ancestor" = any ($4::uuid[])
|
||||
group by
|
||||
"tag_asset"."assetId"
|
||||
having
|
||||
count(distinct "tag_closure"."id_ancestor") = $3
|
||||
count(distinct "tag_closure"."id_ancestor") = $5
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$4
|
||||
$6
|
||||
offset
|
||||
$7
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (has-albums-false)
|
||||
select
|
||||
@@ -1050,6 +1128,10 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and not exists (
|
||||
select
|
||||
from
|
||||
@@ -1061,7 +1143,9 @@ order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$2
|
||||
$4
|
||||
offset
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (is-encoded)
|
||||
select
|
||||
@@ -1098,19 +1182,25 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and exists (
|
||||
select
|
||||
from
|
||||
"asset_file"
|
||||
where
|
||||
"asset_file"."assetId" = "asset"."id"
|
||||
and "asset_file"."type" = $2
|
||||
and "asset_file"."type" = $4
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (number-range)
|
||||
select
|
||||
@@ -1148,14 +1238,20 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset_exif"."fileSizeInByte" <= $2
|
||||
and "asset_exif"."fileSizeInByte" >= $3
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset_exif"."fileSizeInByte" <= $4
|
||||
and "asset_exif"."fileSizeInByte" >= $5
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$4
|
||||
$6
|
||||
offset
|
||||
$7
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (date-eq)
|
||||
select
|
||||
@@ -1192,12 +1288,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and "asset"."fileCreatedAt" = $2
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and "asset"."fileCreatedAt" = $4
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$3
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (date-range)
|
||||
select
|
||||
@@ -1235,14 +1337,20 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."fileCreatedAt" < $2
|
||||
and "asset"."fileCreatedAt" >= $3
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset"."fileCreatedAt" < $4
|
||||
and "asset"."fileCreatedAt" >= $5
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$4
|
||||
$6
|
||||
offset
|
||||
$7
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (order-fileSize-noExif)
|
||||
select
|
||||
@@ -1279,12 +1387,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
"asset_exif"."fileSizeInByte" desc nulls last,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$2
|
||||
$4
|
||||
offset
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (order-rating-withExif)
|
||||
select
|
||||
@@ -1322,12 +1436,18 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
"asset_exif"."rating" asc nulls last,
|
||||
"asset"."id" asc
|
||||
limit
|
||||
$2
|
||||
$4
|
||||
offset
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (or-branches)
|
||||
select
|
||||
@@ -1365,7 +1485,11 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."isFavorite" = $2
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset"."isFavorite" = $4
|
||||
or exists (
|
||||
select
|
||||
from
|
||||
@@ -1373,15 +1497,17 @@ where
|
||||
where
|
||||
"asset_face"."assetId" = "asset"."id"
|
||||
and "asset_face"."deletedAt" is null
|
||||
and "asset_face"."isVisible" = $3
|
||||
and "asset_face"."personId" = any ($4::uuid[])
|
||||
and "asset_face"."isVisible" = $5
|
||||
and "asset_face"."personId" = any ($6::uuid[])
|
||||
)
|
||||
)
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$5
|
||||
$7
|
||||
offset
|
||||
$8
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (or-with-top-level)
|
||||
select
|
||||
@@ -1419,17 +1545,21 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."fileCreatedAt" < $2
|
||||
and "asset"."fileCreatedAt" >= $3
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset"."fileCreatedAt" < $4
|
||||
and "asset"."fileCreatedAt" >= $5
|
||||
and (
|
||||
"asset"."isFavorite" = $4
|
||||
"asset"."isFavorite" = $6
|
||||
or exists (
|
||||
select
|
||||
from
|
||||
"album_asset"
|
||||
where
|
||||
"album_asset"."assetId" = "asset"."id"
|
||||
and "album_asset"."albumId" = any ($5::uuid[])
|
||||
and "album_asset"."albumId" = any ($7::uuid[])
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1437,8 +1567,310 @@ order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$8
|
||||
offset
|
||||
$9
|
||||
|
||||
-- SearchRepository.searchMetadataV3 (cursor-offset)
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and "asset"."isFavorite" = $4
|
||||
order by
|
||||
"asset"."fileCreatedAt" desc,
|
||||
"asset"."id" desc
|
||||
limit
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
|
||||
-- SearchRepository.searchRandomV3 (baseline)
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
random()
|
||||
limit
|
||||
$4
|
||||
|
||||
-- SearchRepository.searchRandomV3 (with-filter)
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and "asset"."isFavorite" = $4
|
||||
order by
|
||||
random()
|
||||
limit
|
||||
$5
|
||||
|
||||
-- SearchRepository.searchSmartV3 (baseline)
|
||||
begin
|
||||
set
|
||||
local vchordrq.probes = 1
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
inner join "smart_search" on "asset"."id" = "smart_search"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
smart_search.embedding <=> $4,
|
||||
"asset"."id" asc
|
||||
limit
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
commit
|
||||
|
||||
-- SearchRepository.searchSmartV3 (with-filter)
|
||||
begin
|
||||
set
|
||||
local vchordrq.probes = 1
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
inner join "smart_search" on "asset"."id" = "smart_search"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset"."fileCreatedAt" < $4
|
||||
and "asset"."fileCreatedAt" >= $5
|
||||
)
|
||||
order by
|
||||
smart_search.embedding <=> $6,
|
||||
"asset"."id" asc
|
||||
limit
|
||||
$7
|
||||
offset
|
||||
$8
|
||||
commit
|
||||
|
||||
-- SearchRepository.searchSmartV3 (cursor-offset)
|
||||
begin
|
||||
set
|
||||
local vchordrq.probes = 1
|
||||
select
|
||||
"asset"."id",
|
||||
"asset"."updateId",
|
||||
"asset"."createdAt",
|
||||
"asset"."updatedAt",
|
||||
"asset"."deletedAt",
|
||||
"asset"."status",
|
||||
"asset"."checksum",
|
||||
"asset"."checksumAlgorithm",
|
||||
"asset"."duplicateId",
|
||||
"asset"."duration",
|
||||
"asset"."fileCreatedAt",
|
||||
"asset"."fileModifiedAt",
|
||||
"asset"."isExternal",
|
||||
"asset"."isFavorite",
|
||||
"asset"."isOffline",
|
||||
"asset"."isEdited",
|
||||
"asset"."visibility",
|
||||
"asset"."libraryId",
|
||||
"asset"."livePhotoVideoId",
|
||||
"asset"."localDateTime",
|
||||
"asset"."originalFileName",
|
||||
"asset"."originalPath",
|
||||
"asset"."ownerId",
|
||||
"asset"."stackId",
|
||||
"asset"."thumbhash",
|
||||
"asset"."type",
|
||||
"asset"."width",
|
||||
"asset"."height"
|
||||
from
|
||||
"asset"
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
inner join "smart_search" on "asset"."id" = "smart_search"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
order by
|
||||
smart_search.embedding <=> $4,
|
||||
"asset"."id" asc
|
||||
limit
|
||||
$5
|
||||
offset
|
||||
$6
|
||||
commit
|
||||
|
||||
-- SearchRepository.searchStatisticsV3 (baseline)
|
||||
select
|
||||
count(*) as "total"
|
||||
@@ -1447,6 +1879,10 @@ from
|
||||
left join "asset_exif" on "asset"."id" = "asset_exif"."assetId"
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and true
|
||||
|
||||
-- SearchRepository.searchStatisticsV3 (with-filter)
|
||||
@@ -1458,9 +1894,13 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset_exif"."fileSizeInByte" >= $2
|
||||
and "asset"."fileCreatedAt" < $3
|
||||
and "asset"."fileCreatedAt" >= $4
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset_exif"."fileSizeInByte" >= $4
|
||||
and "asset"."fileCreatedAt" < $5
|
||||
and "asset"."fileCreatedAt" >= $6
|
||||
)
|
||||
|
||||
-- SearchRepository.searchStatisticsV3 (with-or)
|
||||
@@ -1472,7 +1912,11 @@ from
|
||||
where
|
||||
"asset"."ownerId" = any ($1::uuid[])
|
||||
and (
|
||||
"asset"."isFavorite" = $2
|
||||
"asset"."visibility" != $2
|
||||
or "asset"."ownerId" = $3
|
||||
)
|
||||
and (
|
||||
"asset"."isFavorite" = $4
|
||||
or not exists (
|
||||
select
|
||||
from
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
searchAssetBuilder,
|
||||
searchAssetBuilderLegacy,
|
||||
searchMetadataV3Examples,
|
||||
searchRandomV3Examples,
|
||||
searchSmartV3Examples,
|
||||
searchStatisticsV3Examples,
|
||||
withExifInner,
|
||||
withSearchOrder,
|
||||
@@ -133,10 +135,13 @@ export type AssetSearchOptions = Omit<BaseAssetSearchOptions, 'visibility'> &
|
||||
|
||||
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
|
||||
|
||||
export interface AssetSearchScope {
|
||||
userIds?: string[];
|
||||
lockedOwnerId: string;
|
||||
}
|
||||
|
||||
export interface AssetSearchBuilderV3Options {
|
||||
filter?: SearchFilter;
|
||||
/** Server-derived ownership scope. Never client-controlled. */
|
||||
userIds?: string[];
|
||||
withExif?: boolean;
|
||||
withFaces?: boolean;
|
||||
withPeople?: boolean;
|
||||
@@ -146,6 +151,7 @@ export interface AssetSearchBuilderV3Options {
|
||||
|
||||
export interface AssetSearchPaginationV3Options {
|
||||
size: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export type SmartSearchOptions = SearchDateOptions &
|
||||
@@ -208,6 +214,7 @@ export interface GetCameraLensModelsOptions {
|
||||
export class SearchRepository {
|
||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||
|
||||
// TODO(v4): remove with the deprecated flat-field search API
|
||||
@GenerateSql({
|
||||
params: [
|
||||
{ page: 1, size: 100 },
|
||||
@@ -233,6 +240,7 @@ export class SearchRepository {
|
||||
return paginationHelper(items, pagination.size);
|
||||
}
|
||||
|
||||
// TODO(v4): remove with the deprecated flat-field search API
|
||||
@GenerateSql({
|
||||
params: [
|
||||
{
|
||||
@@ -249,6 +257,7 @@ export class SearchRepository {
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
// TODO(v4): remove with the deprecated flat-field search API
|
||||
@GenerateSql({
|
||||
params: [
|
||||
100,
|
||||
@@ -269,6 +278,7 @@ export class SearchRepository {
|
||||
.execute();
|
||||
}
|
||||
|
||||
// TODO(v4): remove with the deprecated flat-field search API
|
||||
@GenerateSql({
|
||||
params: [
|
||||
100,
|
||||
@@ -292,6 +302,7 @@ export class SearchRepository {
|
||||
.execute();
|
||||
}
|
||||
|
||||
// TODO(v4): remove with the deprecated flat-field search API
|
||||
@GenerateSql({
|
||||
params: [
|
||||
{ page: 1, size: 200 },
|
||||
@@ -518,20 +529,60 @@ export class SearchRepository {
|
||||
return res.map((row) => row.lensModel!);
|
||||
}
|
||||
|
||||
// TODO(v4): drop the V3 suffix once the legacy methods are removed
|
||||
@GenerateSql(...searchMetadataV3Examples)
|
||||
searchMetadataV3(
|
||||
async searchMetadataV3(
|
||||
pagination: AssetSearchPaginationV3Options,
|
||||
options: AssetSearchBuilderV3Options,
|
||||
): Promise<MapAsset[]> {
|
||||
return withSearchOrder(searchAssetBuilder(this.db, options), options.order)
|
||||
scope: AssetSearchScope,
|
||||
) {
|
||||
const items = await withSearchOrder(searchAssetBuilder(this.db, options, scope), options.order)
|
||||
.select(columns.searchAsset)
|
||||
.limit(pagination.size)
|
||||
.limit(pagination.size + 1)
|
||||
.offset(pagination.offset ?? 0)
|
||||
.execute();
|
||||
return paginationHelper(items, pagination.size);
|
||||
}
|
||||
|
||||
// TODO(v4): drop the V3 suffix once the legacy methods are removed
|
||||
@GenerateSql(...searchRandomV3Examples)
|
||||
searchRandomV3(
|
||||
size: number,
|
||||
options: Omit<AssetSearchBuilderV3Options, 'order'>,
|
||||
scope: AssetSearchScope,
|
||||
): Promise<MapAsset[]> {
|
||||
return searchAssetBuilder(this.db, options, scope)
|
||||
.select(columns.searchAsset)
|
||||
.orderBy(sql`random()`)
|
||||
.limit(size)
|
||||
.execute();
|
||||
}
|
||||
|
||||
// TODO(v4): drop the V3 suffix once the legacy methods are removed
|
||||
@GenerateSql(...searchSmartV3Examples)
|
||||
searchSmartV3(
|
||||
pagination: AssetSearchPaginationV3Options,
|
||||
options: Omit<AssetSearchBuilderV3Options, 'order'> & { embedding: string },
|
||||
scope: AssetSearchScope,
|
||||
) {
|
||||
return this.db.transaction().execute(async (trx) => {
|
||||
await sql`set local vchordrq.probes = ${sql.lit(probes[VectorIndex.Clip])}`.execute(trx);
|
||||
const items = await searchAssetBuilder(trx, options, scope)
|
||||
.select(columns.searchAsset)
|
||||
.innerJoin('smart_search', 'asset.id', 'smart_search.assetId')
|
||||
.orderBy(sql`smart_search.embedding <=> ${options.embedding}`)
|
||||
.orderBy('asset.id', 'asc')
|
||||
.limit(pagination.size + 1)
|
||||
.offset(pagination.offset ?? 0)
|
||||
.execute();
|
||||
return paginationHelper(items, pagination.size);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(v4): drop the V3 suffix once the legacy methods are removed
|
||||
@GenerateSql(...searchStatisticsV3Examples)
|
||||
searchStatisticsV3(options: AssetSearchBuilderV3Options) {
|
||||
return searchAssetBuilder(this.db, options)
|
||||
searchStatisticsV3(options: AssetSearchBuilderV3Options, scope: AssetSearchScope) {
|
||||
return searchAssetBuilder(this.db, options, scope)
|
||||
.select((qb) => qb.fn.countAll<number>().as('total'))
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||
import { mapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { SearchSuggestionType } from 'src/dtos/search.dto';
|
||||
import { AssetVisibility } from 'src/enum';
|
||||
import { SearchService } from 'src/services/search.service';
|
||||
import { AssetFactory } from 'test/factories/asset.factory';
|
||||
import { AuthFactory } from 'test/factories/auth.factory';
|
||||
import { authStub } from 'test/fixtures/auth.stub';
|
||||
import { getForAsset } from 'test/mappers';
|
||||
import { newUuid } from 'test/small.factory';
|
||||
import { newTestService, ServiceMocks } from 'test/utils';
|
||||
import { beforeEach, vitest } from 'vitest';
|
||||
|
||||
@@ -215,6 +217,69 @@ describe(SearchService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('new shape routing', () => {
|
||||
it('should route a filter request to the V3 search and a flat request to the legacy search', async () => {
|
||||
const auth = AuthFactory.create();
|
||||
|
||||
mocks.search.searchMetadataV3.mockResolvedValue({ hasNextPage: false, items: [] });
|
||||
await sut.searchMetadata(auth, { size: 250, filter: {} });
|
||||
expect(mocks.search.searchMetadataV3).toHaveBeenCalled();
|
||||
expect(mocks.search.searchMetadata).not.toHaveBeenCalled();
|
||||
|
||||
mocks.search.searchMetadata.mockResolvedValue({ hasNextPage: false, items: [] });
|
||||
await sut.searchMetadata(auth, { size: 250, city: 'Oslo' });
|
||||
expect(mocks.search.searchMetadata).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should route statistics, random, and smart filter requests to their V3 search', async () => {
|
||||
const auth = AuthFactory.create();
|
||||
|
||||
mocks.search.searchStatisticsV3.mockResolvedValue({ total: 0 });
|
||||
await expect(sut.searchStatistics(auth, { filter: {} })).resolves.toEqual({ total: 0 });
|
||||
|
||||
mocks.search.searchRandomV3.mockResolvedValue([]);
|
||||
await expect(sut.searchRandom(auth, { size: 250, filter: {} })).resolves.toEqual([]);
|
||||
|
||||
mocks.search.searchSmartV3.mockResolvedValue({ hasNextPage: false, items: [] });
|
||||
mocks.machineLearning.encodeText.mockResolvedValue('[1, 2, 3]');
|
||||
await sut.searchSmart(auth, { size: 100, filter: {}, query: 'test' });
|
||||
expect(mocks.search.searchSmartV3).toHaveBeenCalledWith(
|
||||
{ size: 100, offset: 0 },
|
||||
expect.objectContaining({ embedding: '[1, 2, 3]' }),
|
||||
expect.objectContaining({ lockedOwnerId: expect.any(String) }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject an invalid cursor', async () => {
|
||||
await expect(sut.searchMetadata(AuthFactory.create(), { size: 250, cursor: '???' })).rejects.toThrowError(
|
||||
new BadRequestException('Invalid cursor'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject an unelevated session whose filter could match locked assets', async () => {
|
||||
const filter = { visibility: { in: [AssetVisibility.Locked, AssetVisibility.Timeline] } };
|
||||
await expect(sut.searchMetadata(AuthFactory.create(), { size: 250, filter })).rejects.toBeInstanceOf(
|
||||
UnauthorizedException,
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject a shared link without a top-level album constraint', async () => {
|
||||
const auth = AuthFactory.from().sharedLink().build();
|
||||
|
||||
await expect(sut.searchMetadata(auth, { size: 250, filter: {} })).rejects.toThrowError(
|
||||
new BadRequestException('Shared link access is only allowed in combination with an albumIds filter'),
|
||||
);
|
||||
|
||||
const albumId = newUuid();
|
||||
mocks.access.album.checkSharedLinkAccess.mockResolvedValue(new Set([albumId]));
|
||||
await expect(
|
||||
sut.searchMetadata(auth, { size: 250, filter: { or: [{ albumIds: { any: [albumId] } }] } }),
|
||||
).rejects.toThrowError(
|
||||
new BadRequestException('Shared link access is only allowed in combination with an albumIds filter'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('searchSmart', () => {
|
||||
beforeEach(() => {
|
||||
mocks.search.searchSmart.mockResolvedValue({ hasNextPage: false, items: [] });
|
||||
@@ -226,7 +291,7 @@ describe(SearchService.name, () => {
|
||||
machineLearning: { enabled: false },
|
||||
});
|
||||
|
||||
await expect(sut.searchSmart(authStub.user1, { query: 'test' })).rejects.toThrowError(
|
||||
await expect(sut.searchSmart(authStub.user1, { size: 100, query: 'test' })).rejects.toThrowError(
|
||||
new BadRequestException('Smart search is not enabled'),
|
||||
);
|
||||
});
|
||||
@@ -236,13 +301,13 @@ describe(SearchService.name, () => {
|
||||
machineLearning: { clip: { enabled: false } },
|
||||
});
|
||||
|
||||
await expect(sut.searchSmart(authStub.user1, { query: 'test' })).rejects.toThrowError(
|
||||
await expect(sut.searchSmart(authStub.user1, { size: 100, query: 'test' })).rejects.toThrowError(
|
||||
new BadRequestException('Smart search is not enabled'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should work', async () => {
|
||||
await sut.searchSmart(authStub.user1, { query: 'test' });
|
||||
await sut.searchSmart(authStub.user1, { size: 100, query: 'test' });
|
||||
|
||||
expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith(
|
||||
'test',
|
||||
@@ -250,7 +315,13 @@ describe(SearchService.name, () => {
|
||||
);
|
||||
expect(mocks.search.searchSmart).toHaveBeenCalledWith(
|
||||
{ page: 1, size: 100 },
|
||||
{ query: 'test', embedding: '[1, 2, 3]', userIds: [authStub.user1.user.id], visibility: 'not-locked' },
|
||||
{
|
||||
query: 'test',
|
||||
size: 100,
|
||||
embedding: '[1, 2, 3]',
|
||||
userIds: [authStub.user1.user.id],
|
||||
visibility: 'not-locked',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -272,7 +343,7 @@ describe(SearchService.name, () => {
|
||||
machineLearning: { clip: { modelName: 'ViT-B-16-SigLIP__webli' } },
|
||||
});
|
||||
|
||||
await sut.searchSmart(authStub.user1, { query: 'test' });
|
||||
await sut.searchSmart(authStub.user1, { size: 100, query: 'test' });
|
||||
|
||||
expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith(
|
||||
'test',
|
||||
@@ -281,7 +352,7 @@ describe(SearchService.name, () => {
|
||||
});
|
||||
|
||||
it('should use language specified in request', async () => {
|
||||
await sut.searchSmart(authStub.user1, { query: 'test', language: 'de' });
|
||||
await sut.searchSmart(authStub.user1, { size: 100, query: 'test', language: 'de' });
|
||||
|
||||
expect(mocks.machineLearning.encodeText).toHaveBeenCalledWith(
|
||||
'test',
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { LRUMap } from 'mnemonist';
|
||||
import { SystemConfig } from 'src/config';
|
||||
import { AssetMapOptions, AssetResponseDto, MapAsset, mapAsset } from 'src/dtos/asset-response.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { mapPerson, PersonResponseDto } from 'src/dtos/person.dto';
|
||||
import {
|
||||
isNewShapeRequest,
|
||||
LargeAssetSearchDto,
|
||||
mapPlaces,
|
||||
MetadataSearchDto,
|
||||
PlacesResponseDto,
|
||||
RandomSearchDto,
|
||||
SearchFilter,
|
||||
SearchPeopleDto,
|
||||
SearchPlacesDto,
|
||||
SearchResponseDto,
|
||||
@@ -19,10 +22,17 @@ import {
|
||||
StatisticsSearchDto,
|
||||
} from 'src/dtos/search.dto';
|
||||
import { AssetOrder, AssetVisibility, Permission } from 'src/enum';
|
||||
import { AssetSearchScope } from 'src/repositories/search.repository';
|
||||
import { BaseService } from 'src/services/base.service';
|
||||
import { requireElevatedPermission } from 'src/utils/access';
|
||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||
import { isSmartSearchEnabled } from 'src/utils/misc';
|
||||
import { decodeSearchCursor, encodeSearchCursor } from 'src/utils/search-cursor';
|
||||
import {
|
||||
applyLockedVisibilityPolicy,
|
||||
collectFilterIds,
|
||||
hasTopLevelPositiveIdsConstraint,
|
||||
} from 'src/utils/search-filter';
|
||||
|
||||
@Injectable()
|
||||
export class SearchService extends BaseService {
|
||||
@@ -63,6 +73,10 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
async searchMetadata(auth: AuthDto, dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
||||
if (isNewShapeRequest(dto)) {
|
||||
return this.searchMetadataV3(auth, dto);
|
||||
}
|
||||
|
||||
if (dto.visibility === AssetVisibility.Locked) {
|
||||
requireElevatedPermission(auth);
|
||||
}
|
||||
@@ -84,7 +98,7 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
const page = dto.page ?? 1;
|
||||
const size = dto.size || 250;
|
||||
const size = dto.size;
|
||||
const { hasNextPage, items } = await this.searchRepository.searchMetadata(
|
||||
{ page, size },
|
||||
{
|
||||
@@ -96,10 +110,14 @@ export class SearchService extends BaseService {
|
||||
},
|
||||
);
|
||||
|
||||
return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });
|
||||
return this.mapResponse(items, { auth }, { nextPage: hasNextPage ? (page + 1).toString() : null });
|
||||
}
|
||||
|
||||
async searchStatistics(auth: AuthDto, dto: StatisticsSearchDto): Promise<SearchStatisticsResponseDto> {
|
||||
if (isNewShapeRequest(dto)) {
|
||||
return this.searchStatisticsV3(auth, dto);
|
||||
}
|
||||
|
||||
const userIds = await this.getUserIdsToSearch(auth, dto.visibility);
|
||||
if (dto.visibility === AssetVisibility.Locked) {
|
||||
requireElevatedPermission(auth);
|
||||
@@ -113,12 +131,16 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
async searchRandom(auth: AuthDto, dto: RandomSearchDto): Promise<AssetResponseDto[]> {
|
||||
if (isNewShapeRequest(dto)) {
|
||||
return this.searchRandomV3(auth, dto);
|
||||
}
|
||||
|
||||
if (dto.visibility === AssetVisibility.Locked) {
|
||||
requireElevatedPermission(auth);
|
||||
}
|
||||
|
||||
const userIds = await this.getUserIdsToSearch(auth, dto.visibility);
|
||||
const items = await this.searchRepository.searchRandom(dto.size || 250, {
|
||||
const items = await this.searchRepository.searchRandom(dto.size, {
|
||||
...dto,
|
||||
visibility: dto.visibility ?? (auth.session?.hasElevatedPermission ? undefined : 'not-locked'),
|
||||
userIds,
|
||||
@@ -132,7 +154,7 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
const userIds = await this.getUserIdsToSearch(auth, dto.visibility);
|
||||
const items = await this.searchRepository.searchLargeAssets(dto.size || 250, {
|
||||
const items = await this.searchRepository.searchLargeAssets(dto.size, {
|
||||
...dto,
|
||||
visibility: dto.visibility ?? (auth.session?.hasElevatedPermission ? undefined : 'not-locked'),
|
||||
userIds,
|
||||
@@ -141,6 +163,10 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
async searchSmart(auth: AuthDto, dto: SmartSearchDto): Promise<SearchResponseDto> {
|
||||
if (isNewShapeRequest(dto)) {
|
||||
return this.searchSmartV3(auth, dto);
|
||||
}
|
||||
|
||||
if (dto.visibility === AssetVisibility.Locked) {
|
||||
requireElevatedPermission(auth);
|
||||
}
|
||||
@@ -151,30 +177,9 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
|
||||
const userIds = this.getUserIdsToSearch(auth, dto.visibility);
|
||||
let embedding;
|
||||
if (dto.query) {
|
||||
const key = machineLearning.clip.modelName + dto.query + dto.language;
|
||||
embedding = this.embeddingCache.get(key);
|
||||
if (!embedding) {
|
||||
embedding = await this.machineLearningRepository.encodeText(dto.query, {
|
||||
modelName: machineLearning.clip.modelName,
|
||||
language: dto.language,
|
||||
});
|
||||
this.embeddingCache.set(key, embedding);
|
||||
}
|
||||
} else if (dto.queryAssetId) {
|
||||
await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.queryAssetId] });
|
||||
const getEmbeddingResponse = await this.searchRepository.getEmbedding(dto.queryAssetId);
|
||||
const assetEmbedding = getEmbeddingResponse?.embedding;
|
||||
if (!assetEmbedding) {
|
||||
throw new BadRequestException(`Asset ${dto.queryAssetId} has no embedding`);
|
||||
}
|
||||
embedding = assetEmbedding;
|
||||
} else {
|
||||
throw new BadRequestException('Either `query` or `queryAssetId` must be set');
|
||||
}
|
||||
const embedding = await this.resolveEmbedding(auth, dto, machineLearning);
|
||||
const page = dto.page ?? 1;
|
||||
const size = dto.size || 100;
|
||||
const size = dto.size;
|
||||
const { hasNextPage, items } = await this.searchRepository.searchSmart(
|
||||
{ page, size },
|
||||
{
|
||||
@@ -185,7 +190,7 @@ export class SearchService extends BaseService {
|
||||
},
|
||||
);
|
||||
|
||||
return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });
|
||||
return this.mapResponse(items, { auth }, { nextPage: hasNextPage ? (page + 1).toString() : null });
|
||||
}
|
||||
|
||||
async getAssetsByCity(auth: AuthDto): Promise<AssetResponseDto[]> {
|
||||
@@ -229,6 +234,122 @@ export class SearchService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
private async searchMetadataV3(auth: AuthDto, dto: MetadataSearchDto): Promise<SearchResponseDto> {
|
||||
const { filter, scope } = await this.resolveSearchScopeV3(auth, dto);
|
||||
|
||||
const { offset } = decodeSearchCursor(dto.cursor);
|
||||
const size = dto.size;
|
||||
const { hasNextPage, items } = await this.searchRepository.searchMetadataV3(
|
||||
{ size, offset },
|
||||
{
|
||||
filter,
|
||||
withExif: dto.withExif,
|
||||
withPeople: dto.withPeople,
|
||||
withStacked: dto.withStacked,
|
||||
order: dto.orderBy,
|
||||
},
|
||||
scope,
|
||||
);
|
||||
|
||||
return this.mapResponse(items, { auth }, { nextCursor: hasNextPage ? encodeSearchCursor(offset + size) : null });
|
||||
}
|
||||
|
||||
private async searchStatisticsV3(auth: AuthDto, dto: StatisticsSearchDto): Promise<SearchStatisticsResponseDto> {
|
||||
const { filter, scope } = await this.resolveSearchScopeV3(auth, dto);
|
||||
return this.searchRepository.searchStatisticsV3({ filter }, scope);
|
||||
}
|
||||
|
||||
private async searchRandomV3(auth: AuthDto, dto: RandomSearchDto): Promise<AssetResponseDto[]> {
|
||||
const { filter, scope } = await this.resolveSearchScopeV3(auth, dto);
|
||||
const items = await this.searchRepository.searchRandomV3(
|
||||
dto.size,
|
||||
{
|
||||
filter,
|
||||
withExif: dto.withExif,
|
||||
withPeople: dto.withPeople,
|
||||
withStacked: dto.withStacked,
|
||||
},
|
||||
scope,
|
||||
);
|
||||
return items.map((item) => mapAsset(item, { auth }));
|
||||
}
|
||||
|
||||
private async searchSmartV3(auth: AuthDto, dto: SmartSearchDto): Promise<SearchResponseDto> {
|
||||
const { machineLearning } = await this.getConfig({ withCache: false });
|
||||
if (!isSmartSearchEnabled(machineLearning)) {
|
||||
throw new BadRequestException('Smart search is not enabled');
|
||||
}
|
||||
|
||||
const [{ filter, scope }, embedding] = await Promise.all([
|
||||
this.resolveSearchScopeV3(auth, dto),
|
||||
this.resolveEmbedding(auth, dto, machineLearning),
|
||||
]);
|
||||
|
||||
const { offset } = decodeSearchCursor(dto.cursor);
|
||||
const size = dto.size;
|
||||
const { hasNextPage, items } = await this.searchRepository.searchSmartV3(
|
||||
{ size, offset },
|
||||
{ filter, withExif: dto.withExif, embedding },
|
||||
scope,
|
||||
);
|
||||
|
||||
return this.mapResponse(items, { auth }, { nextCursor: hasNextPage ? encodeSearchCursor(offset + size) : null });
|
||||
}
|
||||
|
||||
private async resolveSearchScopeV3(
|
||||
auth: AuthDto,
|
||||
dto: { filter?: SearchFilter },
|
||||
): Promise<{ filter: SearchFilter; scope: AssetSearchScope }> {
|
||||
const filter = dto.filter ?? {};
|
||||
const effectiveFilter = applyLockedVisibilityPolicy(auth, filter);
|
||||
|
||||
// only a top-level any/all constrains every result to accessible albums; anything else keeps
|
||||
// the ownership scope so an OR branch cannot widen the search to other users' assets
|
||||
const hasTopLevelAlbums = hasTopLevelPositiveIdsConstraint(filter, 'albumIds');
|
||||
if (auth.sharedLink && !hasTopLevelAlbums) {
|
||||
throw new BadRequestException('Shared link access is only allowed in combination with an albumIds filter');
|
||||
}
|
||||
|
||||
const albumIds = collectFilterIds(filter, 'albumIds');
|
||||
const [userIds] = await Promise.all([
|
||||
hasTopLevelAlbums ? undefined : this.getUserIdsToSearch(auth),
|
||||
albumIds.length > 0 ? this.requireAccess({ auth, ids: albumIds, permission: Permission.AlbumRead }) : undefined,
|
||||
]);
|
||||
|
||||
return { filter: effectiveFilter, scope: { userIds, lockedOwnerId: auth.user.id } };
|
||||
}
|
||||
|
||||
private async resolveEmbedding(
|
||||
auth: AuthDto,
|
||||
dto: SmartSearchDto,
|
||||
machineLearning: SystemConfig['machineLearning'],
|
||||
): Promise<string> {
|
||||
if (dto.query) {
|
||||
const key = machineLearning.clip.modelName + dto.query + dto.language;
|
||||
let embedding = this.embeddingCache.get(key);
|
||||
if (!embedding) {
|
||||
embedding = await this.machineLearningRepository.encodeText(dto.query, {
|
||||
modelName: machineLearning.clip.modelName,
|
||||
language: dto.language,
|
||||
});
|
||||
this.embeddingCache.set(key, embedding);
|
||||
}
|
||||
return embedding;
|
||||
}
|
||||
|
||||
if (dto.queryAssetId) {
|
||||
await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.queryAssetId] });
|
||||
const getEmbeddingResponse = await this.searchRepository.getEmbedding(dto.queryAssetId);
|
||||
const assetEmbedding = getEmbeddingResponse?.embedding;
|
||||
if (!assetEmbedding) {
|
||||
throw new BadRequestException(`Asset ${dto.queryAssetId} has no embedding`);
|
||||
}
|
||||
return assetEmbedding;
|
||||
}
|
||||
|
||||
throw new BadRequestException('Either `query` or `queryAssetId` must be set');
|
||||
}
|
||||
|
||||
private async getUserIdsToSearch(auth: AuthDto, visibility?: AssetVisibility): Promise<string[]> {
|
||||
// Locked assets are personal. Never include partner IDs, regardless of A's elevated session.
|
||||
if (visibility === AssetVisibility.Locked) {
|
||||
@@ -242,7 +363,11 @@ export class SearchService extends BaseService {
|
||||
return [auth.user.id, ...partnerIds];
|
||||
}
|
||||
|
||||
private mapResponse(assets: MapAsset[], nextPage: string | null, options: AssetMapOptions): SearchResponseDto {
|
||||
private mapResponse(
|
||||
assets: MapAsset[],
|
||||
options: AssetMapOptions,
|
||||
page: { nextPage?: string | null; nextCursor?: string | null } = {},
|
||||
): SearchResponseDto {
|
||||
return {
|
||||
albums: { total: 0, count: 0, items: [], facets: [] },
|
||||
assets: {
|
||||
@@ -250,7 +375,8 @@ export class SearchService extends BaseService {
|
||||
count: assets.length,
|
||||
items: assets.map((asset) => mapAsset(asset, options)),
|
||||
facets: [],
|
||||
nextPage,
|
||||
nextPage: page.nextPage ?? null,
|
||||
nextCursor: page.nextCursor ?? null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,11 @@ import {
|
||||
ExifOrientation,
|
||||
SearchOrderField,
|
||||
} from 'src/enum';
|
||||
import { AssetSearchBuilderOptions, AssetSearchBuilderV3Options } from 'src/repositories/search.repository';
|
||||
import {
|
||||
AssetSearchBuilderOptions,
|
||||
AssetSearchBuilderV3Options,
|
||||
AssetSearchScope,
|
||||
} from 'src/repositories/search.repository';
|
||||
import { DB } from 'src/schema';
|
||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||
import { AudioStreamInfo, VectorExtension, VideoFormat, VideoPacketInfo, VideoStreamInfo } from 'src/types';
|
||||
@@ -762,7 +766,7 @@ function branchPredicates(eb: AssetExpressionBuilder, branch: SearchFilterBranch
|
||||
|
||||
// ordering is deliberately left to the caller so aggregate-only consumers (counts, stats)
|
||||
// can compose the same filters without stripping an order by
|
||||
export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuilderV3Options) {
|
||||
export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuilderV3Options, scope: AssetSearchScope) {
|
||||
const filter = options.filter ?? {};
|
||||
|
||||
return (
|
||||
@@ -772,8 +776,9 @@ export function searchAssetBuilder(kysely: Kysely<DB>, options: AssetSearchBuild
|
||||
// postgres eliminates the left join when no exif column is referenced, so unused joins are free
|
||||
.leftJoin('asset_exif', 'asset.id', 'asset_exif.assetId')
|
||||
.$if(!!options.withExif, (qb) => qb.select(selectExifInfo))
|
||||
.$if(!!options.userIds && options.userIds.length > 0, (qb) =>
|
||||
qb.where('asset.ownerId', '=', anyUuid(options.userIds!)),
|
||||
.$if(!!scope.userIds && scope.userIds.length > 0, (qb) => qb.where('asset.ownerId', '=', anyUuid(scope.userIds!)))
|
||||
.where((eb) =>
|
||||
eb.or([eb('asset.visibility', '!=', AssetVisibility.Locked), eb('asset.ownerId', '=', scope.lockedOwnerId)]),
|
||||
)
|
||||
.$if(!!(options.withFaces || options.withPeople), (qb) => qb.select(withFacesAndPeople))
|
||||
.$if(options.withStacked === false, (qb) => qb.where('asset.stackId', 'is', null))
|
||||
@@ -809,86 +814,121 @@ export function withSearchOrder(qb: ReturnType<typeof searchAssetBuilder>, order
|
||||
);
|
||||
}
|
||||
|
||||
const scopeExample: AssetSearchScope = { userIds: [DummyValue.UUID], lockedOwnerId: DummyValue.UUID };
|
||||
|
||||
export const searchMetadataV3Examples: GenerateSqlQueries[] = [
|
||||
{ name: 'baseline', params: [{ size: 100 }, { userIds: [DummyValue.UUID] }] },
|
||||
{ name: 'empty', params: [{ size: 100 }, {}] },
|
||||
{ name: 'baseline', params: [{ size: 100 }, {}, scopeExample] },
|
||||
{ name: 'empty', params: [{ size: 100 }, {}, { lockedOwnerId: DummyValue.UUID }] },
|
||||
{
|
||||
name: 'or-exif-only',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { or: [{ city: { eq: DummyValue.STRING } }] } }],
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
filter: { or: [{ city: { eq: DummyValue.STRING } }] },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'string-eq-null',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { city: { eq: null } } }],
|
||||
params: [{ size: 100 }, { filter: { city: { eq: null } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'string-pattern-like',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { description: { like: DummyValue.STRING } } }],
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
filter: { description: { like: DummyValue.STRING } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'string-pattern-notLike',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { description: { notLike: DummyValue.STRING } } }],
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
filter: { description: { notLike: DummyValue.STRING } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'string-pattern-startsWith',
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{ userIds: [DummyValue.UUID], filter: { originalFileName: { startsWith: DummyValue.STRING } } },
|
||||
{
|
||||
filter: { originalFileName: { startsWith: DummyValue.STRING } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'string-similarity-ocr',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { ocr: { matches: DummyValue.STRING } } }],
|
||||
params: [{ size: 100 }, { filter: { ocr: { matches: DummyValue.STRING } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'ids-any',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { albumIds: { any: [DummyValue.UUID] } } }],
|
||||
params: [{ size: 100 }, { filter: { albumIds: { any: [DummyValue.UUID] } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'ids-all',
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{ userIds: [DummyValue.UUID], filter: { personIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } } },
|
||||
{
|
||||
filter: { personIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'ids-all-single',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { albumIds: { all: [DummyValue.UUID] } } }],
|
||||
params: [{ size: 100 }, { filter: { albumIds: { all: [DummyValue.UUID] } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'ids-none',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { tagIds: { none: [DummyValue.UUID] } } }],
|
||||
params: [{ size: 100 }, { filter: { tagIds: { none: [DummyValue.UUID] } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'ids-tags-all',
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{ userIds: [DummyValue.UUID], filter: { tagIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } } },
|
||||
{
|
||||
filter: { tagIds: { all: [DummyValue.UUID, DummyValue.UUID_1] } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'has-albums-false',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { hasAlbums: { eq: false } } }],
|
||||
params: [{ size: 100 }, { filter: { hasAlbums: { eq: false } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'is-encoded',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { isEncoded: { eq: true } } }],
|
||||
params: [{ size: 100 }, { filter: { isEncoded: { eq: true } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'number-range',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { fileSizeInBytes: { gte: 100, lte: 1000 } } }],
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
filter: { fileSizeInBytes: { gte: 100, lte: 1000 } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'date-eq',
|
||||
params: [{ size: 100 }, { userIds: [DummyValue.UUID], filter: { takenAt: { eq: DummyValue.DATE } } }],
|
||||
params: [{ size: 100 }, { filter: { takenAt: { eq: DummyValue.DATE } } }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'date-range',
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
filter: { takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -896,10 +936,10 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
order: { field: SearchOrderField.FileSizeInBytes, direction: AssetOrder.Desc },
|
||||
withExif: false,
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -907,10 +947,10 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
order: { field: SearchOrderField.Rating, direction: AssetOrder.Asc },
|
||||
withExif: true,
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -918,11 +958,11 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
filter: {
|
||||
or: [{ isFavorite: { eq: true } }, { personIds: { any: [DummyValue.UUID] } }],
|
||||
},
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -930,39 +970,73 @@ export const searchMetadataV3Examples: GenerateSqlQueries[] = [
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
filter: {
|
||||
takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE },
|
||||
or: [{ isFavorite: { eq: true } }, { albumIds: { any: [DummyValue.UUID] } }],
|
||||
},
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'cursor-offset',
|
||||
params: [{ size: 100, offset: 100 }, { filter: { isFavorite: { eq: true } } }, scopeExample],
|
||||
},
|
||||
];
|
||||
|
||||
export const searchRandomV3Examples: GenerateSqlQueries[] = [
|
||||
{ name: 'baseline', params: [100, {}, scopeExample] },
|
||||
{
|
||||
name: 'with-filter',
|
||||
params: [100, { filter: { isFavorite: { eq: true } } }, scopeExample],
|
||||
},
|
||||
];
|
||||
|
||||
export const searchSmartV3Examples: GenerateSqlQueries[] = [
|
||||
{
|
||||
name: 'baseline',
|
||||
params: [{ size: 100 }, { embedding: DummyValue.VECTOR }, scopeExample],
|
||||
},
|
||||
{
|
||||
name: 'with-filter',
|
||||
params: [
|
||||
{ size: 100 },
|
||||
{
|
||||
embedding: DummyValue.VECTOR,
|
||||
filter: { takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE } },
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'cursor-offset',
|
||||
params: [{ size: 100, offset: 100 }, { embedding: DummyValue.VECTOR }, scopeExample],
|
||||
},
|
||||
];
|
||||
|
||||
export const searchStatisticsV3Examples: GenerateSqlQueries[] = [
|
||||
{ name: 'baseline', params: [{ userIds: [DummyValue.UUID] }] },
|
||||
{ name: 'baseline', params: [{}, scopeExample] },
|
||||
{
|
||||
name: 'with-filter',
|
||||
params: [
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
filter: {
|
||||
takenAt: { gte: DummyValue.DATE, lt: DummyValue.DATE },
|
||||
fileSizeInBytes: { gte: 100 },
|
||||
},
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'with-or',
|
||||
params: [
|
||||
{
|
||||
userIds: [DummyValue.UUID],
|
||||
filter: {
|
||||
or: [{ isFavorite: { eq: true } }, { hasAlbums: { eq: false } }],
|
||||
},
|
||||
},
|
||||
scopeExample,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
38
server/src/utils/search-cursor.spec.ts
Normal file
38
server/src/utils/search-cursor.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { decodeSearchCursor, encodeSearchCursor } from 'src/utils/search-cursor';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('encodeSearchCursor', () => {
|
||||
it('should produce an opaque base64url string', () => {
|
||||
const cursor = encodeSearchCursor(250);
|
||||
expect(cursor).toMatch(/^[\w-]+$/);
|
||||
});
|
||||
|
||||
it('should round-trip an offset', () => {
|
||||
for (const offset of [0, 1, 250, 1_000_000]) {
|
||||
expect(decodeSearchCursor(encodeSearchCursor(offset))).toEqual({ offset });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeSearchCursor', () => {
|
||||
const encode = (value: unknown) => Buffer.from(JSON.stringify(value)).toString('base64url');
|
||||
|
||||
it('should treat a missing cursor as the first page', () => {
|
||||
expect(decodeSearchCursor(undefined)).toEqual({ offset: 0 });
|
||||
});
|
||||
|
||||
it.each([
|
||||
['empty string', ''],
|
||||
['garbage that is not base64url', '!!!not-base64!!!'],
|
||||
['base64 of non-JSON', Buffer.from('not json').toString('base64url')],
|
||||
['JSON without an offset', encode({})],
|
||||
['JSON with a non-object payload', encode(42)],
|
||||
['null payload', encode(null)],
|
||||
['negative offset', encode({ o: -1 })],
|
||||
['fractional offset', encode({ o: 1.5 })],
|
||||
['string offset', encode({ o: '5' })],
|
||||
])('should reject %s', (_, cursor) => {
|
||||
expect(() => decodeSearchCursor(cursor)).toThrowError(new BadRequestException('Invalid cursor'));
|
||||
});
|
||||
});
|
||||
22
server/src/utils/search-cursor.ts
Normal file
22
server/src/utils/search-cursor.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import z from 'zod';
|
||||
|
||||
const SearchCursorPayloadSchema = z.object({
|
||||
o: z.int().min(0),
|
||||
});
|
||||
|
||||
export const encodeSearchCursor = (offset: number): string =>
|
||||
Buffer.from(JSON.stringify({ o: offset } satisfies z.infer<typeof SearchCursorPayloadSchema>)).toString('base64url');
|
||||
|
||||
export const decodeSearchCursor = (cursor?: string): { offset: number } => {
|
||||
if (cursor === undefined) {
|
||||
return { offset: 0 };
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = SearchCursorPayloadSchema.parse(JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')));
|
||||
return { offset: payload.o };
|
||||
} catch {
|
||||
throw new BadRequestException('Invalid cursor');
|
||||
}
|
||||
};
|
||||
102
server/src/utils/search-filter.spec.ts
Normal file
102
server/src/utils/search-filter.spec.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { UnauthorizedException } from '@nestjs/common';
|
||||
import { SearchFilter } from 'src/dtos/search.dto';
|
||||
import { AssetVisibility } from 'src/enum';
|
||||
import {
|
||||
applyLockedVisibilityPolicy,
|
||||
collectFilterIds,
|
||||
hasTopLevelPositiveIdsConstraint,
|
||||
} from 'src/utils/search-filter';
|
||||
import { AuthFactory } from 'test/factories/auth.factory';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const elevatedAuth = () => AuthFactory.from().session({ hasElevatedPermission: true }).build();
|
||||
const unelevatedAuth = () => AuthFactory.from().session().build();
|
||||
|
||||
describe(applyLockedVisibilityPolicy.name, () => {
|
||||
it('should let an elevated session query locked assets', () => {
|
||||
const filter = { visibility: { eq: AssetVisibility.Locked } };
|
||||
expect(applyLockedVisibilityPolicy(elevatedAuth(), filter)).toBe(filter);
|
||||
});
|
||||
|
||||
it('should reject an unelevated session when any operator permits locked', () => {
|
||||
for (const visibility of [
|
||||
{ eq: AssetVisibility.Locked },
|
||||
{ ne: AssetVisibility.Timeline },
|
||||
{ in: [AssetVisibility.Locked, AssetVisibility.Timeline] },
|
||||
{ notIn: [AssetVisibility.Timeline] },
|
||||
]) {
|
||||
expect(() => applyLockedVisibilityPolicy(unelevatedAuth(), { visibility })).toThrow(UnauthorizedException);
|
||||
}
|
||||
});
|
||||
|
||||
it('should keep a filter whose top-level visibility excludes locked', () => {
|
||||
for (const visibility of [
|
||||
{ eq: AssetVisibility.Timeline },
|
||||
{ ne: AssetVisibility.Locked },
|
||||
{ in: [AssetVisibility.Timeline, AssetVisibility.Archive] },
|
||||
{ notIn: [AssetVisibility.Locked] },
|
||||
]) {
|
||||
const filter = { visibility };
|
||||
expect(applyLockedVisibilityPolicy(unelevatedAuth(), filter)).toBe(filter);
|
||||
}
|
||||
});
|
||||
|
||||
it('should let a safe top-level visibility decide over could-match branches', () => {
|
||||
const filter = { visibility: { ne: AssetVisibility.Locked }, or: [{ visibility: { eq: AssetVisibility.Locked } }] };
|
||||
expect(applyLockedVisibilityPolicy(unelevatedAuth(), filter)).toBe(filter);
|
||||
});
|
||||
|
||||
it('should reject a branch that permits locked when the top level has no visibility', () => {
|
||||
const filter = {
|
||||
or: [{ city: { eq: 'Oslo' } }, { visibility: { in: [AssetVisibility.Locked, AssetVisibility.Timeline] } }],
|
||||
};
|
||||
expect(() => applyLockedVisibilityPolicy(unelevatedAuth(), filter)).toThrow(UnauthorizedException);
|
||||
});
|
||||
|
||||
it('should otherwise inject visibility != locked without mutating the input', () => {
|
||||
const filter = { city: { eq: 'Oslo' }, visibility: undefined };
|
||||
expect(applyLockedVisibilityPolicy(unelevatedAuth(), filter)).toEqual({
|
||||
city: { eq: 'Oslo' },
|
||||
visibility: { ne: AssetVisibility.Locked },
|
||||
});
|
||||
expect(filter.visibility).toBeUndefined();
|
||||
|
||||
const branched = { or: [{ isFavorite: { eq: true } }, { visibility: { eq: AssetVisibility.Timeline } }] };
|
||||
expect(applyLockedVisibilityPolicy(unelevatedAuth(), branched).visibility).toEqual({ ne: AssetVisibility.Locked });
|
||||
});
|
||||
});
|
||||
|
||||
describe(collectFilterIds.name, () => {
|
||||
it('should union and dedupe ids across operators and branches', () => {
|
||||
const [albumA, albumB, albumC] = [
|
||||
'00000000-0000-4000-8000-00000000000a',
|
||||
'00000000-0000-4000-8000-00000000000b',
|
||||
'00000000-0000-4000-8000-00000000000c',
|
||||
];
|
||||
const filter: SearchFilter = {
|
||||
albumIds: { any: [albumA], none: [albumB] },
|
||||
or: [{ albumIds: { all: [albumC, albumA] } }, { city: { eq: 'Oslo' } }],
|
||||
};
|
||||
expect(collectFilterIds(filter, 'albumIds').toSorted()).toEqual([albumA, albumB, albumC]);
|
||||
expect(collectFilterIds({}, 'albumIds')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should only collect ids of the requested field', () => {
|
||||
const albumId = '00000000-0000-4000-8000-00000000000a';
|
||||
const personId = '00000000-0000-4000-8000-00000000000b';
|
||||
const filter = { albumIds: { any: [albumId] }, personIds: { any: [personId] } };
|
||||
expect(collectFilterIds(filter, 'personIds')).toEqual([personId]);
|
||||
});
|
||||
});
|
||||
|
||||
describe(hasTopLevelPositiveIdsConstraint.name, () => {
|
||||
it('should detect only top-level any/all constraints', () => {
|
||||
const albumId = '00000000-0000-4000-8000-00000000000a';
|
||||
expect(hasTopLevelPositiveIdsConstraint({ albumIds: { any: [albumId] } }, 'albumIds')).toBe(true);
|
||||
expect(hasTopLevelPositiveIdsConstraint({ albumIds: { all: [albumId] } }, 'albumIds')).toBe(true);
|
||||
expect(hasTopLevelPositiveIdsConstraint({ albumIds: { none: [albumId] } }, 'albumIds')).toBe(false);
|
||||
expect(hasTopLevelPositiveIdsConstraint({ or: [{ albumIds: { any: [albumId] } }] }, 'albumIds')).toBe(false);
|
||||
expect(hasTopLevelPositiveIdsConstraint({ albumIds: { any: [albumId] } }, 'tagIds')).toBe(false);
|
||||
expect(hasTopLevelPositiveIdsConstraint({}, 'albumIds')).toBe(false);
|
||||
});
|
||||
});
|
||||
70
server/src/utils/search-filter.ts
Normal file
70
server/src/utils/search-filter.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { SearchFilter, SearchFilterBranch } from 'src/dtos/search.dto';
|
||||
import { AssetVisibility } from 'src/enum';
|
||||
import { requireElevatedPermission } from 'src/utils/access';
|
||||
|
||||
type EnumField = 'type' | 'visibility';
|
||||
type EnumOperator = keyof NonNullable<SearchFilterBranch[EnumField]>;
|
||||
type EnumOperandMap<T> = { eq: T; ne: T; in: T[]; notIn: T[] };
|
||||
type EnumCondition<T> = { [K in EnumOperator]?: EnumOperandMap<T>[K] };
|
||||
type IdsFilterField = 'albumIds' | 'personIds' | 'tagIds';
|
||||
|
||||
const filterBranches = (filter: SearchFilter): SearchFilterBranch[] => [filter, ...(filter.or ?? [])];
|
||||
|
||||
/** Whether a row with `value` can satisfy the condition. A missing operator allows any value. */
|
||||
const canMatch = <T>(condition: EnumCondition<T>, value: T): boolean =>
|
||||
(condition.eq === undefined || condition.eq === value) &&
|
||||
(condition.ne === undefined || condition.ne !== value) &&
|
||||
(condition.in === undefined || condition.in.includes(value)) &&
|
||||
(condition.notIn === undefined || !condition.notIn.includes(value));
|
||||
|
||||
/**
|
||||
* The conditions that decide which `field` values the filter can return. A top-level condition decides alone, otherwise each branch itself.
|
||||
*/
|
||||
const decidingConditions = <F extends EnumField>(filter: SearchFilter, field: F) => {
|
||||
if (filter[field] !== undefined) {
|
||||
return [filter[field]];
|
||||
}
|
||||
|
||||
return filterBranches(filter)
|
||||
.map((branch) => branch[field])
|
||||
.filter((condition) => condition !== undefined);
|
||||
};
|
||||
|
||||
/**
|
||||
* Keeps locked assets out of search results unless the session is elevated: a filter that asks for
|
||||
* them is rejected with 401, and any other filter gets `visibility != locked` ANDed in.
|
||||
*/
|
||||
export const applyLockedVisibilityPolicy = (auth: AuthDto, filter: SearchFilter): SearchFilter => {
|
||||
if (auth.session?.hasElevatedPermission) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
if (decidingConditions(filter, 'visibility').some((condition) => canMatch(condition, AssetVisibility.Locked))) {
|
||||
requireElevatedPermission(auth);
|
||||
}
|
||||
|
||||
if (filter.visibility !== undefined) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
return { ...filter, visibility: { ne: AssetVisibility.Locked } };
|
||||
};
|
||||
|
||||
export const collectFilterIds = (filter: SearchFilter, field: IdsFilterField): string[] => {
|
||||
const ids = new Set<string>();
|
||||
|
||||
for (const branch of filterBranches(filter)) {
|
||||
for (const operator of ['any', 'all', 'none'] as const) {
|
||||
for (const id of branch[field]?.[operator] ?? []) {
|
||||
ids.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...ids];
|
||||
};
|
||||
|
||||
/** Whether the top level constrains results to specific `field` ids (`any`/`all`; `none` only excludes). */
|
||||
export const hasTopLevelPositiveIdsConstraint = (filter: SearchFilter, field: IdsFilterField): boolean =>
|
||||
filter[field]?.any !== undefined || filter[field]?.all !== undefined;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Kysely } from 'kysely';
|
||||
import { SearchSuggestionType } from 'src/dtos/search.dto';
|
||||
import { AlbumUserRole, AssetVisibility } from 'src/enum';
|
||||
import { AlbumUserRole, AssetOrder, AssetVisibility, SearchOrderField } from 'src/enum';
|
||||
import { AccessRepository } from 'src/repositories/access.repository';
|
||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
||||
import { DatabaseRepository } from 'src/repositories/database.repository';
|
||||
@@ -16,6 +16,8 @@ import { getKyselyDB } from 'test/utils';
|
||||
|
||||
let defaultDatabase: Kysely<DB>;
|
||||
|
||||
const unitVector = (index: number) => JSON.stringify(Array.from({ length: 512 }, (_, i) => (i === index ? 1 : 0)));
|
||||
|
||||
const setup = (db?: Kysely<DB>) => {
|
||||
return newMediumService(SearchService, {
|
||||
database: db || defaultDatabase,
|
||||
@@ -56,7 +58,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
await expect(sut.searchLargeAssets(auth, {})).resolves.toEqual([
|
||||
await expect(sut.searchLargeAssets(auth, { size: 250 })).resolves.toEqual([
|
||||
expect.objectContaining({ id: assets[2].id }),
|
||||
expect.objectContaining({ id: assets[0].id }),
|
||||
expect.objectContaining({ id: assets[1].id }),
|
||||
@@ -120,7 +122,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const response = await sut.searchMetadata(auth, { withStacked: false });
|
||||
const response = await sut.searchMetadata(auth, { size: 250, withStacked: false });
|
||||
|
||||
expect(response.assets.items.length).toBe(1);
|
||||
expect(response.assets.items[0].id).toBe(unstackedAsset.id);
|
||||
@@ -135,7 +137,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const response = await sut.searchMetadata(auth, { withStacked: false });
|
||||
const response = await sut.searchMetadata(auth, { size: 250, withStacked: false });
|
||||
|
||||
expect(response.assets.items.length).toBe(0);
|
||||
});
|
||||
@@ -148,7 +150,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id }, session: { hasElevatedPermission: true } });
|
||||
|
||||
const response = await sut.searchMetadata(auth, { withStacked: false });
|
||||
const response = await sut.searchMetadata(auth, { size: 250, withStacked: false });
|
||||
|
||||
expect(response.assets.items.length).toBe(1);
|
||||
});
|
||||
@@ -168,7 +170,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const response = await sut.searchMetadata(auth, { albumIds: [album.id] });
|
||||
const response = await sut.searchMetadata(auth, { size: 250, albumIds: [album.id] });
|
||||
|
||||
expect(response.assets.items.length).toBe(1);
|
||||
});
|
||||
@@ -186,7 +188,7 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
await expect(sut.searchMetadata(auth, { albumIds: [album.id] })).rejects.toThrow(
|
||||
await expect(sut.searchMetadata(auth, { size: 250, albumIds: [album.id] })).rejects.toThrow(
|
||||
'Not found or no album.read access',
|
||||
);
|
||||
});
|
||||
@@ -222,9 +224,196 @@ describe(SearchService.name, () => {
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const response = await sut.searchRandom(auth, {});
|
||||
const response = await sut.searchRandom(auth, { size: 250 });
|
||||
|
||||
expect(response.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('new search shape', () => {
|
||||
it('should filter by an exif field and return a cursor-less single page', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, city: 'Oslo' });
|
||||
const { asset: other } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: other.id, city: 'Bergen' });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
const response = await sut.searchMetadata(auth, { size: 250, filter: { city: { eq: 'Oslo' } } });
|
||||
|
||||
expect(response.assets.items).toEqual([expect.objectContaining({ id: asset.id })]);
|
||||
expect(response.assets.nextPage).toBeNull();
|
||||
expect(response.assets.nextCursor).toBeNull();
|
||||
});
|
||||
|
||||
it('should combine OR branches', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset: oslo } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: oslo.id, city: 'Oslo' });
|
||||
const { asset: favorite } = await ctx.newAsset({ ownerId: user.id, isFavorite: true });
|
||||
await ctx.newAsset({ ownerId: user.id });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
const response = await sut.searchMetadata(auth, {
|
||||
size: 250,
|
||||
filter: { or: [{ city: { eq: 'Oslo' } }, { isFavorite: { eq: true } }] },
|
||||
});
|
||||
|
||||
expect(response.assets.items.map(({ id }) => id).toSorted()).toEqual([oslo.id, favorite.id].toSorted());
|
||||
});
|
||||
|
||||
it('should widen to album assets only for a top-level album constraint', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { user: otherUser } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: otherUser.id });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id });
|
||||
await ctx.newAlbumAsset({ albumId: album.id, assetId: asset.id });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const topLevel = await sut.searchMetadata(auth, { size: 250, filter: { albumIds: { any: [album.id] } } });
|
||||
expect(topLevel.assets.items).toEqual([expect.objectContaining({ id: asset.id })]);
|
||||
|
||||
const branchOnly = await sut.searchMetadata(auth, {
|
||||
size: 250,
|
||||
filter: { or: [{ albumIds: { any: [album.id] } }] },
|
||||
});
|
||||
expect(branchOnly.assets.items).toEqual([]);
|
||||
});
|
||||
|
||||
it('should reject an inaccessible album anywhere in the filter', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { user: otherUser } = await ctx.newUser();
|
||||
const { album } = await ctx.newAlbum({ ownerId: otherUser.id });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
await expect(
|
||||
sut.searchMetadata(auth, { size: 250, filter: { or: [{ albumIds: { none: [album.id] } }] } }),
|
||||
).rejects.toThrow('Not found or no album.read access');
|
||||
});
|
||||
|
||||
it('should return locked assets only to an elevated session that asks for them', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset: timeline } = await ctx.newAsset({ ownerId: user.id });
|
||||
const { asset: locked } = await ctx.newAsset({ ownerId: user.id, visibility: AssetVisibility.Locked });
|
||||
|
||||
const unelevated = await sut.searchMetadata(factory.auth({ user: { id: user.id } }), { size: 250, filter: {} });
|
||||
expect(unelevated.assets.items).toEqual([expect.objectContaining({ id: timeline.id })]);
|
||||
|
||||
const elevatedAuth = factory.auth({ user: { id: user.id }, session: { hasElevatedPermission: true } });
|
||||
const elevated = await sut.searchMetadata(elevatedAuth, {
|
||||
size: 250,
|
||||
filter: { visibility: { eq: AssetVisibility.Locked } },
|
||||
});
|
||||
expect(elevated.assets.items).toEqual([expect.objectContaining({ id: locked.id })]);
|
||||
});
|
||||
|
||||
it('should exclude partner assets from a locked-only search', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { user: partner } = await ctx.newUser();
|
||||
await ctx.newPartner({ sharedById: partner.id, sharedWithId: user.id });
|
||||
const { asset: ownLocked } = await ctx.newAsset({ ownerId: user.id, visibility: AssetVisibility.Locked });
|
||||
await ctx.newAsset({ ownerId: partner.id, visibility: AssetVisibility.Locked });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id }, session: { hasElevatedPermission: true } });
|
||||
const response = await sut.searchMetadata(auth, {
|
||||
size: 250,
|
||||
filter: { visibility: { eq: AssetVisibility.Locked } },
|
||||
});
|
||||
|
||||
expect(response.assets.items).toEqual([expect.objectContaining({ id: ownLocked.id })]);
|
||||
});
|
||||
|
||||
it('should never return partner locked assets, even for locked-matching mixed filters', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const { user: partner } = await ctx.newUser();
|
||||
await ctx.newPartner({ sharedById: partner.id, sharedWithId: user.id });
|
||||
const { asset: ownLocked } = await ctx.newAsset({ ownerId: user.id, visibility: AssetVisibility.Locked });
|
||||
const { asset: partnerTimeline } = await ctx.newAsset({ ownerId: partner.id });
|
||||
await ctx.newAsset({ ownerId: partner.id, visibility: AssetVisibility.Locked });
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id }, session: { hasElevatedPermission: true } });
|
||||
const response = await sut.searchMetadata(auth, {
|
||||
size: 250,
|
||||
filter: { visibility: { in: [AssetVisibility.Locked, AssetVisibility.Timeline] } },
|
||||
});
|
||||
|
||||
const ids = response.assets.items.map(({ id }) => id);
|
||||
expect(ids.toSorted()).toEqual([ownLocked.id, partnerTimeline.id].toSorted());
|
||||
});
|
||||
|
||||
it('should paginate with an opaque cursor', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await ctx.newAsset({ ownerId: user.id });
|
||||
}
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
|
||||
const firstPage = await sut.searchMetadata(auth, { filter: {}, size: 2 });
|
||||
expect(firstPage.assets.items.length).toBe(2);
|
||||
expect(firstPage.assets.nextPage).toBeNull();
|
||||
expect(firstPage.assets.nextCursor).toEqual(expect.any(String));
|
||||
|
||||
const secondPage = await sut.searchMetadata(auth, { cursor: firstPage.assets.nextCursor!, size: 2 });
|
||||
expect(secondPage.assets.items.length).toBe(1);
|
||||
expect(secondPage.assets.nextCursor).toBeNull();
|
||||
|
||||
const ids = [...firstPage.assets.items, ...secondPage.assets.items].map(({ id }) => id);
|
||||
expect(new Set(ids).size).toBe(3);
|
||||
});
|
||||
|
||||
it('should order by fileSizeInBytes', async () => {
|
||||
const { sut, ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const sizes = [12_334, 599, 123_456];
|
||||
const assetIds: string[] = [];
|
||||
for (const fileSizeInByte of sizes) {
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await ctx.newExif({ assetId: asset.id, fileSizeInByte });
|
||||
assetIds.push(asset.id);
|
||||
}
|
||||
|
||||
const auth = factory.auth({ user: { id: user.id } });
|
||||
const response = await sut.searchMetadata(auth, {
|
||||
size: 250,
|
||||
orderBy: { field: SearchOrderField.FileSizeInBytes, direction: AssetOrder.Asc },
|
||||
});
|
||||
|
||||
expect(response.assets.items.map(({ id }) => id)).toEqual([assetIds[1], assetIds[0], assetIds[2]]);
|
||||
});
|
||||
|
||||
it('should order smart search results by embedding distance with cursor offsets', async () => {
|
||||
const { ctx } = setup();
|
||||
const { user } = await ctx.newUser();
|
||||
const searchRepository = ctx.get(SearchRepository);
|
||||
|
||||
const assetIds: string[] = [];
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
await searchRepository.upsert(asset.id, unitVector(i));
|
||||
assetIds.push(asset.id);
|
||||
}
|
||||
|
||||
const options = { filter: {}, embedding: unitVector(0) };
|
||||
const scope = { userIds: [user.id], lockedOwnerId: user.id };
|
||||
const firstPage = await searchRepository.searchSmartV3({ size: 2 }, options, scope);
|
||||
expect(firstPage.items.length).toBe(2);
|
||||
expect(firstPage.items[0].id).toBe(assetIds[0]);
|
||||
expect(firstPage.hasNextPage).toBe(true);
|
||||
|
||||
const secondPage = await searchRepository.searchSmartV3({ size: 2, offset: 2 }, options, scope);
|
||||
expect(secondPage.items.length).toBe(1);
|
||||
expect(secondPage.hasNextPage).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user