mirror of
https://github.com/immich-app/immich.git
synced 2025-03-11 02:23:09 -05:00
feat(mobile)!: batched full/initial sync (#4840)
* feat(mobile): batched full/initial sync * use OptionalBetween * skip/take as integer --------- Co-authored-by: Fynn Petersen-Frey <zoodyy@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
26fd9d7e5f
commit
21f2d3058a
10 changed files with 189 additions and 63 deletions
56
cli/src/api/open-api/api.ts
generated
56
cli/src/api/open-api/api.ts
generated
|
@ -6695,16 +6695,18 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get all AssetEntity belong to the user
|
* Get all AssetEntity belong to the user
|
||||||
|
* @param {number} [skip]
|
||||||
|
* @param {number} [take]
|
||||||
* @param {string} [userId]
|
* @param {string} [userId]
|
||||||
* @param {boolean} [isFavorite]
|
* @param {boolean} [isFavorite]
|
||||||
* @param {boolean} [isArchived]
|
* @param {boolean} [isArchived]
|
||||||
* @param {number} [skip]
|
|
||||||
* @param {string} [updatedAfter]
|
* @param {string} [updatedAfter]
|
||||||
|
* @param {string} [updatedBefore]
|
||||||
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
getAllAssets: async (userId?: string, isFavorite?: boolean, isArchived?: boolean, skip?: number, updatedAfter?: string, ifNoneMatch?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
getAllAssets: async (skip?: number, take?: number, userId?: string, isFavorite?: boolean, isArchived?: boolean, updatedAfter?: string, updatedBefore?: string, ifNoneMatch?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
const localVarPath = `/asset`;
|
const localVarPath = `/asset`;
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
@ -6726,6 +6728,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
// http bearer authentication required
|
// http bearer authentication required
|
||||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
if (skip !== undefined) {
|
||||||
|
localVarQueryParameter['skip'] = skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (take !== undefined) {
|
||||||
|
localVarQueryParameter['take'] = take;
|
||||||
|
}
|
||||||
|
|
||||||
if (userId !== undefined) {
|
if (userId !== undefined) {
|
||||||
localVarQueryParameter['userId'] = userId;
|
localVarQueryParameter['userId'] = userId;
|
||||||
}
|
}
|
||||||
|
@ -6738,16 +6748,18 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
localVarQueryParameter['isArchived'] = isArchived;
|
localVarQueryParameter['isArchived'] = isArchived;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip !== undefined) {
|
|
||||||
localVarQueryParameter['skip'] = skip;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatedAfter !== undefined) {
|
if (updatedAfter !== undefined) {
|
||||||
localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ?
|
localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ?
|
||||||
(updatedAfter as any).toISOString() :
|
(updatedAfter as any).toISOString() :
|
||||||
updatedAfter;
|
updatedAfter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updatedBefore !== undefined) {
|
||||||
|
localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ?
|
||||||
|
(updatedBefore as any).toISOString() :
|
||||||
|
updatedBefore;
|
||||||
|
}
|
||||||
|
|
||||||
if (ifNoneMatch != null) {
|
if (ifNoneMatch != null) {
|
||||||
localVarHeaderParameter['if-none-match'] = String(ifNoneMatch);
|
localVarHeaderParameter['if-none-match'] = String(ifNoneMatch);
|
||||||
}
|
}
|
||||||
|
@ -8066,17 +8078,19 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get all AssetEntity belong to the user
|
* Get all AssetEntity belong to the user
|
||||||
|
* @param {number} [skip]
|
||||||
|
* @param {number} [take]
|
||||||
* @param {string} [userId]
|
* @param {string} [userId]
|
||||||
* @param {boolean} [isFavorite]
|
* @param {boolean} [isFavorite]
|
||||||
* @param {boolean} [isArchived]
|
* @param {boolean} [isArchived]
|
||||||
* @param {number} [skip]
|
|
||||||
* @param {string} [updatedAfter]
|
* @param {string} [updatedAfter]
|
||||||
|
* @param {string} [updatedBefore]
|
||||||
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async getAllAssets(userId?: string, isFavorite?: boolean, isArchived?: boolean, skip?: number, updatedAfter?: string, ifNoneMatch?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
|
async getAllAssets(skip?: number, take?: number, userId?: string, isFavorite?: boolean, isArchived?: boolean, updatedAfter?: string, updatedBefore?: string, ifNoneMatch?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(skip, take, userId, isFavorite, isArchived, updatedAfter, updatedBefore, ifNoneMatch, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -8421,7 +8435,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
|
getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
|
||||||
return localVarFp.getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.skip, requestParameters.updatedAfter, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath));
|
return localVarFp.getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get a single asset\'s information
|
* Get a single asset\'s information
|
||||||
|
@ -8719,6 +8733,20 @@ export interface AssetApiDownloadFileRequest {
|
||||||
* @interface AssetApiGetAllAssetsRequest
|
* @interface AssetApiGetAllAssetsRequest
|
||||||
*/
|
*/
|
||||||
export interface AssetApiGetAllAssetsRequest {
|
export interface AssetApiGetAllAssetsRequest {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof AssetApiGetAllAssets
|
||||||
|
*/
|
||||||
|
readonly skip?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof AssetApiGetAllAssets
|
||||||
|
*/
|
||||||
|
readonly take?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -8742,17 +8770,17 @@ export interface AssetApiGetAllAssetsRequest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {string}
|
||||||
* @memberof AssetApiGetAllAssets
|
* @memberof AssetApiGetAllAssets
|
||||||
*/
|
*/
|
||||||
readonly skip?: number
|
readonly updatedAfter?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof AssetApiGetAllAssets
|
* @memberof AssetApiGetAllAssets
|
||||||
*/
|
*/
|
||||||
readonly updatedAfter?: string
|
readonly updatedBefore?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ETag of data already cached on the client
|
* ETag of data already cached on the client
|
||||||
|
@ -9430,7 +9458,7 @@ export class AssetApi extends BaseAPI {
|
||||||
* @memberof AssetApi
|
* @memberof AssetApi
|
||||||
*/
|
*/
|
||||||
public getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig) {
|
public getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig) {
|
||||||
return AssetApiFp(this.configuration).getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.skip, requestParameters.updatedAfter, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
|
return AssetApiFp(this.configuration).getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,20 +62,31 @@ class AssetService {
|
||||||
|
|
||||||
/// Returns `null` if the server state did not change, else list of assets
|
/// Returns `null` if the server state did not change, else list of assets
|
||||||
Future<List<Asset>?> _getRemoteAssets(User user) async {
|
Future<List<Asset>?> _getRemoteAssets(User user) async {
|
||||||
|
const int chunkSize = 5000;
|
||||||
try {
|
try {
|
||||||
|
final DateTime now = DateTime.now().toUtc();
|
||||||
|
final List<Asset> allAssets = [];
|
||||||
|
for (int i = 0;; i += chunkSize) {
|
||||||
final List<AssetResponseDto>? assets =
|
final List<AssetResponseDto>? assets =
|
||||||
await _apiService.assetApi.getAllAssets(
|
await _apiService.assetApi.getAllAssets(
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
// updatedBefore is important! without it we could
|
||||||
|
// a) get the same Asset multiple times in different versions (when
|
||||||
|
// the asset is modified while the chunks are loaded from the server)
|
||||||
|
// b) miss assets when new assets are inserted in between the calls
|
||||||
|
updatedBefore: now,
|
||||||
|
skip: i,
|
||||||
|
take: chunkSize,
|
||||||
);
|
);
|
||||||
if (assets == null) {
|
if (assets == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (assets.isNotEmpty && assets.first.ownerId != user.id) {
|
|
||||||
log.warning("Make sure that server and app versions match!"
|
|
||||||
" The server returned assets for user ${assets.first.ownerId}"
|
|
||||||
" while requesting assets of user ${user.id}");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return assets.map(Asset.remote).toList();
|
allAssets.addAll(assets.map(Asset.remote));
|
||||||
|
if (assets.length < chunkSize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allAssets;
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
log.severe(
|
log.severe(
|
||||||
'Error while getting remote assets: ${error.toString()}',
|
'Error while getting remote assets: ${error.toString()}',
|
||||||
|
|
|
@ -197,7 +197,7 @@ class SyncService {
|
||||||
User user,
|
User user,
|
||||||
FutureOr<List<Asset>?> Function(User user) loadAssets,
|
FutureOr<List<Asset>?> Function(User user) loadAssets,
|
||||||
) async {
|
) async {
|
||||||
final DateTime now = DateTime.now();
|
final DateTime now = DateTime.now().toUtc();
|
||||||
final List<Asset>? remote = await loadAssets(user);
|
final List<Asset>? remote = await loadAssets(user);
|
||||||
if (remote == null) {
|
if (remote == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -210,6 +210,10 @@ class SyncService {
|
||||||
assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
|
assert(inDb.isSorted(Asset.compareByChecksum), "inDb not sorted!");
|
||||||
|
|
||||||
remote.sort(Asset.compareByChecksum);
|
remote.sort(Asset.compareByChecksum);
|
||||||
|
|
||||||
|
// filter our duplicates that might be introduced by the chunked retrieval
|
||||||
|
remote.uniqueConsecutive(compare: Asset.compareByChecksum);
|
||||||
|
|
||||||
final (toAdd, toUpdate, toRemove) = _diffAssets(remote, inDb, remote: true);
|
final (toAdd, toUpdate, toRemove) = _diffAssets(remote, inDb, remote: true);
|
||||||
if (toAdd.isEmpty && toUpdate.isEmpty && toRemove.isEmpty) {
|
if (toAdd.isEmpty && toUpdate.isEmpty && toRemove.isEmpty) {
|
||||||
await _updateUserAssetsETag(user, now);
|
await _updateUserAssetsETag(user, now);
|
||||||
|
@ -759,6 +763,12 @@ class SyncService {
|
||||||
final List<Asset> toAdd = [];
|
final List<Asset> toAdd = [];
|
||||||
final List<Asset> toUpdate = [];
|
final List<Asset> toUpdate = [];
|
||||||
final List<Asset> toRemove = [];
|
final List<Asset> toRemove = [];
|
||||||
|
if (assets.isEmpty || inDb.isEmpty) {
|
||||||
|
// fast path for trivial cases: halfes memory usage during initial sync
|
||||||
|
return assets.isEmpty
|
||||||
|
? (toAdd, toUpdate, inDb) // remove all from DB
|
||||||
|
: (assets, toUpdate, toRemove); // add all assets
|
||||||
|
}
|
||||||
diffSortedListsSync(
|
diffSortedListsSync(
|
||||||
inDb,
|
inDb,
|
||||||
assets,
|
assets,
|
||||||
|
|
12
mobile/openapi/doc/AssetApi.md
generated
12
mobile/openapi/doc/AssetApi.md
generated
|
@ -374,7 +374,7 @@ void (empty response body)
|
||||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||||
|
|
||||||
# **getAllAssets**
|
# **getAllAssets**
|
||||||
> List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch)
|
> List<AssetResponseDto> getAllAssets(skip, take, userId, isFavorite, isArchived, updatedAfter, updatedBefore, ifNoneMatch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -399,15 +399,17 @@ import 'package:openapi/api.dart';
|
||||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||||
|
|
||||||
final api_instance = AssetApi();
|
final api_instance = AssetApi();
|
||||||
|
final skip = 56; // int |
|
||||||
|
final take = 56; // int |
|
||||||
final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||||
final isFavorite = true; // bool |
|
final isFavorite = true; // bool |
|
||||||
final isArchived = true; // bool |
|
final isArchived = true; // bool |
|
||||||
final skip = 8.14; // num |
|
|
||||||
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
|
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||||
|
final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||||
final ifNoneMatch = ifNoneMatch_example; // String | ETag of data already cached on the client
|
final ifNoneMatch = ifNoneMatch_example; // String | ETag of data already cached on the client
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final result = api_instance.getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch);
|
final result = api_instance.getAllAssets(skip, take, userId, isFavorite, isArchived, updatedAfter, updatedBefore, ifNoneMatch);
|
||||||
print(result);
|
print(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Exception when calling AssetApi->getAllAssets: $e\n');
|
print('Exception when calling AssetApi->getAllAssets: $e\n');
|
||||||
|
@ -418,11 +420,13 @@ try {
|
||||||
|
|
||||||
Name | Type | Description | Notes
|
Name | Type | Description | Notes
|
||||||
------------- | ------------- | ------------- | -------------
|
------------- | ------------- | ------------- | -------------
|
||||||
|
**skip** | **int**| | [optional]
|
||||||
|
**take** | **int**| | [optional]
|
||||||
**userId** | **String**| | [optional]
|
**userId** | **String**| | [optional]
|
||||||
**isFavorite** | **bool**| | [optional]
|
**isFavorite** | **bool**| | [optional]
|
||||||
**isArchived** | **bool**| | [optional]
|
**isArchived** | **bool**| | [optional]
|
||||||
**skip** | **num**| | [optional]
|
|
||||||
**updatedAfter** | **DateTime**| | [optional]
|
**updatedAfter** | **DateTime**| | [optional]
|
||||||
|
**updatedBefore** | **DateTime**| | [optional]
|
||||||
**ifNoneMatch** | **String**| ETag of data already cached on the client | [optional]
|
**ifNoneMatch** | **String**| ETag of data already cached on the client | [optional]
|
||||||
|
|
||||||
### Return type
|
### Return type
|
||||||
|
|
34
mobile/openapi/lib/api/asset_api.dart
generated
34
mobile/openapi/lib/api/asset_api.dart
generated
|
@ -309,19 +309,23 @@ class AssetApi {
|
||||||
///
|
///
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
/// * [int] skip:
|
||||||
|
///
|
||||||
|
/// * [int] take:
|
||||||
|
///
|
||||||
/// * [String] userId:
|
/// * [String] userId:
|
||||||
///
|
///
|
||||||
/// * [bool] isFavorite:
|
/// * [bool] isFavorite:
|
||||||
///
|
///
|
||||||
/// * [bool] isArchived:
|
/// * [bool] isArchived:
|
||||||
///
|
///
|
||||||
/// * [num] skip:
|
|
||||||
///
|
|
||||||
/// * [DateTime] updatedAfter:
|
/// * [DateTime] updatedAfter:
|
||||||
///
|
///
|
||||||
|
/// * [DateTime] updatedBefore:
|
||||||
|
///
|
||||||
/// * [String] ifNoneMatch:
|
/// * [String] ifNoneMatch:
|
||||||
/// ETag of data already cached on the client
|
/// ETag of data already cached on the client
|
||||||
Future<Response> getAllAssetsWithHttpInfo({ String? userId, bool? isFavorite, bool? isArchived, num? skip, DateTime? updatedAfter, String? ifNoneMatch, }) async {
|
Future<Response> getAllAssetsWithHttpInfo({ int? skip, int? take, String? userId, bool? isFavorite, bool? isArchived, DateTime? updatedAfter, DateTime? updatedBefore, String? ifNoneMatch, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final path = r'/asset';
|
final path = r'/asset';
|
||||||
|
|
||||||
|
@ -332,6 +336,12 @@ class AssetApi {
|
||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (skip != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'skip', skip));
|
||||||
|
}
|
||||||
|
if (take != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'take', take));
|
||||||
|
}
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
queryParams.addAll(_queryParams('', 'userId', userId));
|
queryParams.addAll(_queryParams('', 'userId', userId));
|
||||||
}
|
}
|
||||||
|
@ -341,12 +351,12 @@ class AssetApi {
|
||||||
if (isArchived != null) {
|
if (isArchived != null) {
|
||||||
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
|
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
|
||||||
}
|
}
|
||||||
if (skip != null) {
|
|
||||||
queryParams.addAll(_queryParams('', 'skip', skip));
|
|
||||||
}
|
|
||||||
if (updatedAfter != null) {
|
if (updatedAfter != null) {
|
||||||
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
|
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
|
||||||
}
|
}
|
||||||
|
if (updatedBefore != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
|
||||||
|
}
|
||||||
|
|
||||||
if (ifNoneMatch != null) {
|
if (ifNoneMatch != null) {
|
||||||
headerParams[r'if-none-match'] = parameterToString(ifNoneMatch);
|
headerParams[r'if-none-match'] = parameterToString(ifNoneMatch);
|
||||||
|
@ -370,20 +380,24 @@ class AssetApi {
|
||||||
///
|
///
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
/// * [int] skip:
|
||||||
|
///
|
||||||
|
/// * [int] take:
|
||||||
|
///
|
||||||
/// * [String] userId:
|
/// * [String] userId:
|
||||||
///
|
///
|
||||||
/// * [bool] isFavorite:
|
/// * [bool] isFavorite:
|
||||||
///
|
///
|
||||||
/// * [bool] isArchived:
|
/// * [bool] isArchived:
|
||||||
///
|
///
|
||||||
/// * [num] skip:
|
|
||||||
///
|
|
||||||
/// * [DateTime] updatedAfter:
|
/// * [DateTime] updatedAfter:
|
||||||
///
|
///
|
||||||
|
/// * [DateTime] updatedBefore:
|
||||||
|
///
|
||||||
/// * [String] ifNoneMatch:
|
/// * [String] ifNoneMatch:
|
||||||
/// ETag of data already cached on the client
|
/// ETag of data already cached on the client
|
||||||
Future<List<AssetResponseDto>?> getAllAssets({ String? userId, bool? isFavorite, bool? isArchived, num? skip, DateTime? updatedAfter, String? ifNoneMatch, }) async {
|
Future<List<AssetResponseDto>?> getAllAssets({ int? skip, int? take, String? userId, bool? isFavorite, bool? isArchived, DateTime? updatedAfter, DateTime? updatedBefore, String? ifNoneMatch, }) async {
|
||||||
final response = await getAllAssetsWithHttpInfo( userId: userId, isFavorite: isFavorite, isArchived: isArchived, skip: skip, updatedAfter: updatedAfter, ifNoneMatch: ifNoneMatch, );
|
final response = await getAllAssetsWithHttpInfo( skip: skip, take: take, userId: userId, isFavorite: isFavorite, isArchived: isArchived, updatedAfter: updatedAfter, updatedBefore: updatedBefore, ifNoneMatch: ifNoneMatch, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|
2
mobile/openapi/test/asset_api_test.dart
generated
2
mobile/openapi/test/asset_api_test.dart
generated
|
@ -53,7 +53,7 @@ void main() {
|
||||||
|
|
||||||
// Get all AssetEntity belong to the user
|
// Get all AssetEntity belong to the user
|
||||||
//
|
//
|
||||||
//Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, DateTime updatedAfter, String ifNoneMatch }) async
|
//Future<List<AssetResponseDto>> getAllAssets({ int skip, int take, String userId, bool isFavorite, bool isArchived, DateTime updatedAfter, DateTime updatedBefore, String ifNoneMatch }) async
|
||||||
test('test getAllAssets', () async {
|
test('test getAllAssets', () async {
|
||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
|
@ -914,6 +914,22 @@
|
||||||
"description": "Get all AssetEntity belong to the user",
|
"description": "Get all AssetEntity belong to the user",
|
||||||
"operationId": "getAllAssets",
|
"operationId": "getAllAssets",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "skip",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "take",
|
||||||
|
"required": false,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "userId",
|
"name": "userId",
|
||||||
"required": false,
|
"required": false,
|
||||||
|
@ -940,15 +956,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "skip",
|
"name": "updatedAfter",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "number"
|
"format": "date-time",
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "updatedAfter",
|
"name": "updatedBefore",
|
||||||
"required": false,
|
"required": false,
|
||||||
"in": "query",
|
"in": "query",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { AssetCreate } from '@app/domain';
|
import { AssetCreate } from '@app/domain';
|
||||||
import { AssetEntity } from '@app/infra/entities';
|
import { AssetEntity } from '@app/infra/entities';
|
||||||
|
import OptionalBetween from '@app/infra/utils/optional-between.util';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { MoreThan } from 'typeorm';
|
|
||||||
import { In } from 'typeorm/find-options/operator/In';
|
import { In } from 'typeorm/find-options/operator/In';
|
||||||
import { Repository } from 'typeorm/repository/Repository';
|
import { Repository } from 'typeorm/repository/Repository';
|
||||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||||
|
@ -129,7 +129,7 @@ export class AssetRepository implements IAssetRepository {
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
isFavorite: dto.isFavorite,
|
isFavorite: dto.isFavorite,
|
||||||
isArchived: dto.isArchived,
|
isArchived: dto.isArchived,
|
||||||
updatedAt: dto.updatedAfter ? MoreThan(dto.updatedAfter) : undefined,
|
updatedAt: OptionalBetween(dto.updatedAfter, dto.updatedBefore),
|
||||||
},
|
},
|
||||||
relations: {
|
relations: {
|
||||||
exifInfo: true,
|
exifInfo: true,
|
||||||
|
@ -137,6 +137,7 @@ export class AssetRepository implements IAssetRepository {
|
||||||
stack: true,
|
stack: true,
|
||||||
},
|
},
|
||||||
skip: dto.skip || 0,
|
skip: dto.skip || 0,
|
||||||
|
take: dto.take,
|
||||||
order: {
|
order: {
|
||||||
fileCreatedAt: 'DESC',
|
fileCreatedAt: 'DESC',
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Optional, toBoolean } from '@app/domain';
|
import { Optional, toBoolean } from '@app/domain';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Transform, Type } from 'class-transformer';
|
import { Transform, Type } from 'class-transformer';
|
||||||
import { IsBoolean, IsDate, IsNotEmpty, IsNumber, IsUUID } from 'class-validator';
|
import { IsBoolean, IsDate, IsInt, IsNotEmpty, IsUUID } from 'class-validator';
|
||||||
|
|
||||||
export class AssetSearchDto {
|
export class AssetSearchDto {
|
||||||
@Optional()
|
@Optional()
|
||||||
|
@ -17,9 +17,17 @@ export class AssetSearchDto {
|
||||||
isArchived?: boolean;
|
isArchived?: boolean;
|
||||||
|
|
||||||
@Optional()
|
@Optional()
|
||||||
@IsNumber()
|
@IsInt()
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiProperty({ type: 'integer' })
|
||||||
skip?: number;
|
skip?: number;
|
||||||
|
|
||||||
|
@Optional()
|
||||||
|
@IsInt()
|
||||||
|
@Type(() => Number)
|
||||||
|
@ApiProperty({ type: 'integer' })
|
||||||
|
take?: number;
|
||||||
|
|
||||||
@Optional()
|
@Optional()
|
||||||
@IsUUID('4')
|
@IsUUID('4')
|
||||||
@ApiProperty({ format: 'uuid' })
|
@ApiProperty({ format: 'uuid' })
|
||||||
|
@ -29,4 +37,9 @@ export class AssetSearchDto {
|
||||||
@IsDate()
|
@IsDate()
|
||||||
@Type(() => Date)
|
@Type(() => Date)
|
||||||
updatedAfter?: Date;
|
updatedAfter?: Date;
|
||||||
|
|
||||||
|
@Optional()
|
||||||
|
@IsDate()
|
||||||
|
@Type(() => Date)
|
||||||
|
updatedBefore?: Date;
|
||||||
}
|
}
|
||||||
|
|
56
web/src/api/open-api/api.ts
generated
56
web/src/api/open-api/api.ts
generated
|
@ -6695,16 +6695,18 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get all AssetEntity belong to the user
|
* Get all AssetEntity belong to the user
|
||||||
|
* @param {number} [skip]
|
||||||
|
* @param {number} [take]
|
||||||
* @param {string} [userId]
|
* @param {string} [userId]
|
||||||
* @param {boolean} [isFavorite]
|
* @param {boolean} [isFavorite]
|
||||||
* @param {boolean} [isArchived]
|
* @param {boolean} [isArchived]
|
||||||
* @param {number} [skip]
|
|
||||||
* @param {string} [updatedAfter]
|
* @param {string} [updatedAfter]
|
||||||
|
* @param {string} [updatedBefore]
|
||||||
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
getAllAssets: async (userId?: string, isFavorite?: boolean, isArchived?: boolean, skip?: number, updatedAfter?: string, ifNoneMatch?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
getAllAssets: async (skip?: number, take?: number, userId?: string, isFavorite?: boolean, isArchived?: boolean, updatedAfter?: string, updatedBefore?: string, ifNoneMatch?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
const localVarPath = `/asset`;
|
const localVarPath = `/asset`;
|
||||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||||
|
@ -6726,6 +6728,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
// http bearer authentication required
|
// http bearer authentication required
|
||||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||||
|
|
||||||
|
if (skip !== undefined) {
|
||||||
|
localVarQueryParameter['skip'] = skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (take !== undefined) {
|
||||||
|
localVarQueryParameter['take'] = take;
|
||||||
|
}
|
||||||
|
|
||||||
if (userId !== undefined) {
|
if (userId !== undefined) {
|
||||||
localVarQueryParameter['userId'] = userId;
|
localVarQueryParameter['userId'] = userId;
|
||||||
}
|
}
|
||||||
|
@ -6738,16 +6748,18 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
|
||||||
localVarQueryParameter['isArchived'] = isArchived;
|
localVarQueryParameter['isArchived'] = isArchived;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skip !== undefined) {
|
|
||||||
localVarQueryParameter['skip'] = skip;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatedAfter !== undefined) {
|
if (updatedAfter !== undefined) {
|
||||||
localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ?
|
localVarQueryParameter['updatedAfter'] = (updatedAfter as any instanceof Date) ?
|
||||||
(updatedAfter as any).toISOString() :
|
(updatedAfter as any).toISOString() :
|
||||||
updatedAfter;
|
updatedAfter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updatedBefore !== undefined) {
|
||||||
|
localVarQueryParameter['updatedBefore'] = (updatedBefore as any instanceof Date) ?
|
||||||
|
(updatedBefore as any).toISOString() :
|
||||||
|
updatedBefore;
|
||||||
|
}
|
||||||
|
|
||||||
if (ifNoneMatch != null) {
|
if (ifNoneMatch != null) {
|
||||||
localVarHeaderParameter['if-none-match'] = String(ifNoneMatch);
|
localVarHeaderParameter['if-none-match'] = String(ifNoneMatch);
|
||||||
}
|
}
|
||||||
|
@ -8066,17 +8078,19 @@ export const AssetApiFp = function(configuration?: Configuration) {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get all AssetEntity belong to the user
|
* Get all AssetEntity belong to the user
|
||||||
|
* @param {number} [skip]
|
||||||
|
* @param {number} [take]
|
||||||
* @param {string} [userId]
|
* @param {string} [userId]
|
||||||
* @param {boolean} [isFavorite]
|
* @param {boolean} [isFavorite]
|
||||||
* @param {boolean} [isArchived]
|
* @param {boolean} [isArchived]
|
||||||
* @param {number} [skip]
|
|
||||||
* @param {string} [updatedAfter]
|
* @param {string} [updatedAfter]
|
||||||
|
* @param {string} [updatedBefore]
|
||||||
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
* @param {string} [ifNoneMatch] ETag of data already cached on the client
|
||||||
* @param {*} [options] Override http request option.
|
* @param {*} [options] Override http request option.
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
async getAllAssets(userId?: string, isFavorite?: boolean, isArchived?: boolean, skip?: number, updatedAfter?: string, ifNoneMatch?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
|
async getAllAssets(skip?: number, take?: number, userId?: string, isFavorite?: boolean, isArchived?: boolean, updatedAfter?: string, updatedBefore?: string, ifNoneMatch?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<AssetResponseDto>>> {
|
||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(userId, isFavorite, isArchived, skip, updatedAfter, ifNoneMatch, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.getAllAssets(skip, take, userId, isFavorite, isArchived, updatedAfter, updatedBefore, ifNoneMatch, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -8421,7 +8435,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
|
||||||
* @throws {RequiredError}
|
* @throws {RequiredError}
|
||||||
*/
|
*/
|
||||||
getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
|
getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<AssetResponseDto>> {
|
||||||
return localVarFp.getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.skip, requestParameters.updatedAfter, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath));
|
return localVarFp.getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(axios, basePath));
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Get a single asset\'s information
|
* Get a single asset\'s information
|
||||||
|
@ -8719,6 +8733,20 @@ export interface AssetApiDownloadFileRequest {
|
||||||
* @interface AssetApiGetAllAssetsRequest
|
* @interface AssetApiGetAllAssetsRequest
|
||||||
*/
|
*/
|
||||||
export interface AssetApiGetAllAssetsRequest {
|
export interface AssetApiGetAllAssetsRequest {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof AssetApiGetAllAssets
|
||||||
|
*/
|
||||||
|
readonly skip?: number
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @memberof AssetApiGetAllAssets
|
||||||
|
*/
|
||||||
|
readonly take?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
@ -8742,17 +8770,17 @@ export interface AssetApiGetAllAssetsRequest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {string}
|
||||||
* @memberof AssetApiGetAllAssets
|
* @memberof AssetApiGetAllAssets
|
||||||
*/
|
*/
|
||||||
readonly skip?: number
|
readonly updatedAfter?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @memberof AssetApiGetAllAssets
|
* @memberof AssetApiGetAllAssets
|
||||||
*/
|
*/
|
||||||
readonly updatedAfter?: string
|
readonly updatedBefore?: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ETag of data already cached on the client
|
* ETag of data already cached on the client
|
||||||
|
@ -9430,7 +9458,7 @@ export class AssetApi extends BaseAPI {
|
||||||
* @memberof AssetApi
|
* @memberof AssetApi
|
||||||
*/
|
*/
|
||||||
public getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig) {
|
public getAllAssets(requestParameters: AssetApiGetAllAssetsRequest = {}, options?: AxiosRequestConfig) {
|
||||||
return AssetApiFp(this.configuration).getAllAssets(requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.skip, requestParameters.updatedAfter, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
|
return AssetApiFp(this.configuration).getAllAssets(requestParameters.skip, requestParameters.take, requestParameters.userId, requestParameters.isFavorite, requestParameters.isArchived, requestParameters.updatedAfter, requestParameters.updatedBefore, requestParameters.ifNoneMatch, options).then((request) => request(this.axios, this.basePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue