Compare commits
38 commits
main
...
dev/smart-
Author | SHA1 | Date | |
---|---|---|---|
|
bd865d02b3 | ||
|
7bc8918067 | ||
|
29e103d96d | ||
|
e8fdddf08e | ||
|
947132ac77 | ||
|
b7b3735c40 | ||
|
89021ce995 | ||
|
e00b37f4d2 | ||
|
7e28522cb1 | ||
|
e682de67fa | ||
|
1eb3cdca42 | ||
|
e2ad4ac5b3 | ||
|
a7b62a5ad3 | ||
|
9d23d37601 | ||
|
66ebdf1556 | ||
|
f7eab1cc9c | ||
|
048dbc83ba | ||
|
3576d078cf | ||
|
9bd2934aa2 | ||
|
ac18f7515a | ||
|
968fc77183 | ||
|
207049df00 | ||
|
980c6b0dbb | ||
|
ea6bc8fb10 | ||
|
6e2624da7c | ||
|
a37adc0c96 | ||
|
37b4f8455b | ||
|
3c3de8b2af | ||
|
fa8ce261d8 | ||
|
69c95e2b73 | ||
|
51ffac5d15 | ||
|
70cf100407 | ||
|
22e5c6ead2 | ||
|
4a34198b78 | ||
|
a85780c930 | ||
|
717dfe51e7 | ||
|
5168278215 | ||
|
d92dbbe655 |
83 changed files with 3657 additions and 36 deletions
520
cli/src/api/open-api/api.ts
generated
520
cli/src/api/open-api/api.ts
generated
|
@ -252,6 +252,12 @@ export interface AlbumResponseDto {
|
|||
* @memberof AlbumResponseDto
|
||||
*/
|
||||
'ownerId': string;
|
||||
/**
|
||||
*
|
||||
* @type {Array<RuleResponseDto>}
|
||||
* @memberof AlbumResponseDto
|
||||
*/
|
||||
'rules': Array<RuleResponseDto>;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
|
@ -325,6 +331,12 @@ export interface AllJobStatusResponseDto {
|
|||
* @memberof AllJobStatusResponseDto
|
||||
*/
|
||||
'sidecar': JobStatusDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobStatusDto}
|
||||
* @memberof AllJobStatusResponseDto
|
||||
*/
|
||||
'smartAlbum': JobStatusDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobStatusDto}
|
||||
|
@ -946,6 +958,33 @@ export interface CreateProfileImageResponseDto {
|
|||
*/
|
||||
'userId': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface CreateRuleDto
|
||||
*/
|
||||
export interface CreateRuleDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'albumId': string;
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -1509,7 +1548,8 @@ export const JobName = {
|
|||
BackgroundTask: 'backgroundTask',
|
||||
StorageTemplateMigration: 'storageTemplateMigration',
|
||||
Search: 'search',
|
||||
Sidecar: 'sidecar'
|
||||
Sidecar: 'sidecar',
|
||||
SmartAlbum: 'smartAlbum'
|
||||
} as const;
|
||||
|
||||
export type JobName = typeof JobName[keyof typeof JobName];
|
||||
|
@ -1904,6 +1944,59 @@ export interface QueueStatusDto {
|
|||
*/
|
||||
'isPaused': boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @enum {string}
|
||||
*/
|
||||
|
||||
export const RuleKey = {
|
||||
Person: 'person',
|
||||
TakenAfter: 'taken-after',
|
||||
City: 'city',
|
||||
State: 'state',
|
||||
Country: 'country',
|
||||
CameraMake: 'camera-make',
|
||||
CameraModel: 'camera-model',
|
||||
Location: 'location'
|
||||
} as const;
|
||||
|
||||
export type RuleKey = typeof RuleKey[keyof typeof RuleKey];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface RuleResponseDto
|
||||
*/
|
||||
export interface RuleResponseDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'ownerId': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -2635,6 +2728,12 @@ export interface SystemConfigJobDto {
|
|||
* @memberof SystemConfigJobDto
|
||||
*/
|
||||
'sidecar': JobSettingsDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobSettingsDto}
|
||||
* @memberof SystemConfigJobDto
|
||||
*/
|
||||
'smartAlbum': JobSettingsDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobSettingsDto}
|
||||
|
@ -3020,6 +3119,27 @@ export interface UpdateAssetDto {
|
|||
*/
|
||||
'tagIds'?: Array<string>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface UpdateRuleDto
|
||||
*/
|
||||
export interface UpdateRuleDto {
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof UpdateRuleDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof UpdateRuleDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -9700,6 +9820,404 @@ export class PersonApi extends BaseAPI {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* RuleApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {CreateRuleDto} createRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createRule: async (createRuleDto: CreateRuleDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'createRuleDto' is not null or undefined
|
||||
assertParamExists('createRule', 'createRuleDto', createRuleDto)
|
||||
const localVarPath = `/rule`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(createRuleDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRule: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('getRule', 'id', id)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
removeRule: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('removeRule', 'id', id)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {UpdateRuleDto} updateRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateRule: async (id: string, updateRuleDto: UpdateRuleDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('updateRule', 'id', id)
|
||||
// verify required parameter 'updateRuleDto' is not null or undefined
|
||||
assertParamExists('updateRule', 'updateRuleDto', updateRuleDto)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(updateRuleDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RuleApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = RuleApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {CreateRuleDto} createRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async createRule(createRuleDto: CreateRuleDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.createRule(createRuleDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getRule(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getRule(id, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async removeRule(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.removeRule(id, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {UpdateRuleDto} updateRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async updateRule(id: string, updateRuleDto: UpdateRuleDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateRule(id, updateRuleDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RuleApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = RuleApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiCreateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createRule(requestParameters: RuleApiCreateRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.createRule(requestParameters.createRuleDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiGetRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRule(requestParameters: RuleApiGetRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.getRule(requestParameters.id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiRemoveRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
removeRule(requestParameters: RuleApiRemoveRuleRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.removeRule(requestParameters.id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiUpdateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateRule(requestParameters: RuleApiUpdateRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.updateRule(requestParameters.id, requestParameters.updateRuleDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Request parameters for createRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiCreateRuleRequest
|
||||
*/
|
||||
export interface RuleApiCreateRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {CreateRuleDto}
|
||||
* @memberof RuleApiCreateRule
|
||||
*/
|
||||
readonly createRuleDto: CreateRuleDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for getRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiGetRuleRequest
|
||||
*/
|
||||
export interface RuleApiGetRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiGetRule
|
||||
*/
|
||||
readonly id: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for removeRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiRemoveRuleRequest
|
||||
*/
|
||||
export interface RuleApiRemoveRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiRemoveRule
|
||||
*/
|
||||
readonly id: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for updateRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiUpdateRuleRequest
|
||||
*/
|
||||
export interface RuleApiUpdateRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiUpdateRule
|
||||
*/
|
||||
readonly id: string
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {UpdateRuleDto}
|
||||
* @memberof RuleApiUpdateRule
|
||||
*/
|
||||
readonly updateRuleDto: UpdateRuleDto
|
||||
}
|
||||
|
||||
/**
|
||||
* RuleApi - object-oriented interface
|
||||
* @export
|
||||
* @class RuleApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class RuleApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiCreateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public createRule(requestParameters: RuleApiCreateRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).createRule(requestParameters.createRuleDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiGetRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public getRule(requestParameters: RuleApiGetRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).getRule(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiRemoveRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public removeRule(requestParameters: RuleApiRemoveRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).removeRule(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiUpdateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public updateRule(requestParameters: RuleApiUpdateRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).updateRule(requestParameters.id, requestParameters.updateRuleDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SearchApi - axios parameter creator
|
||||
* @export
|
||||
|
|
15
mobile/openapi/.openapi-generator/FILES
generated
15
mobile/openapi/.openapi-generator/FILES
generated
|
@ -38,6 +38,7 @@ doc/CheckExistingAssetsDto.md
|
|||
doc/CheckExistingAssetsResponseDto.md
|
||||
doc/CreateAlbumDto.md
|
||||
doc/CreateProfileImageResponseDto.md
|
||||
doc/CreateRuleDto.md
|
||||
doc/CreateTagDto.md
|
||||
doc/CreateUserDto.md
|
||||
doc/CuratedLocationsResponseDto.md
|
||||
|
@ -75,6 +76,9 @@ doc/PersonApi.md
|
|||
doc/PersonResponseDto.md
|
||||
doc/PersonUpdateDto.md
|
||||
doc/QueueStatusDto.md
|
||||
doc/RuleApi.md
|
||||
doc/RuleKey.md
|
||||
doc/RuleResponseDto.md
|
||||
doc/SearchAlbumResponseDto.md
|
||||
doc/SearchApi.md
|
||||
doc/SearchAssetDto.md
|
||||
|
@ -118,6 +122,7 @@ doc/TranscodeHWAccel.md
|
|||
doc/TranscodePolicy.md
|
||||
doc/UpdateAlbumDto.md
|
||||
doc/UpdateAssetDto.md
|
||||
doc/UpdateRuleDto.md
|
||||
doc/UpdateTagDto.md
|
||||
doc/UpdateUserDto.md
|
||||
doc/UsageByUserDto.md
|
||||
|
@ -136,6 +141,7 @@ lib/api/job_api.dart
|
|||
lib/api/o_auth_api.dart
|
||||
lib/api/partner_api.dart
|
||||
lib/api/person_api.dart
|
||||
lib/api/rule_api.dart
|
||||
lib/api/search_api.dart
|
||||
lib/api/server_info_api.dart
|
||||
lib/api/shared_link_api.dart
|
||||
|
@ -181,6 +187,7 @@ lib/model/check_existing_assets_dto.dart
|
|||
lib/model/check_existing_assets_response_dto.dart
|
||||
lib/model/create_album_dto.dart
|
||||
lib/model/create_profile_image_response_dto.dart
|
||||
lib/model/create_rule_dto.dart
|
||||
lib/model/create_tag_dto.dart
|
||||
lib/model/create_user_dto.dart
|
||||
lib/model/curated_locations_response_dto.dart
|
||||
|
@ -214,6 +221,8 @@ lib/model/people_update_item.dart
|
|||
lib/model/person_response_dto.dart
|
||||
lib/model/person_update_dto.dart
|
||||
lib/model/queue_status_dto.dart
|
||||
lib/model/rule_key.dart
|
||||
lib/model/rule_response_dto.dart
|
||||
lib/model/search_album_response_dto.dart
|
||||
lib/model/search_asset_dto.dart
|
||||
lib/model/search_asset_response_dto.dart
|
||||
|
@ -252,6 +261,7 @@ lib/model/transcode_hw_accel.dart
|
|||
lib/model/transcode_policy.dart
|
||||
lib/model/update_album_dto.dart
|
||||
lib/model/update_asset_dto.dart
|
||||
lib/model/update_rule_dto.dart
|
||||
lib/model/update_tag_dto.dart
|
||||
lib/model/update_user_dto.dart
|
||||
lib/model/usage_by_user_dto.dart
|
||||
|
@ -295,6 +305,7 @@ test/check_existing_assets_dto_test.dart
|
|||
test/check_existing_assets_response_dto_test.dart
|
||||
test/create_album_dto_test.dart
|
||||
test/create_profile_image_response_dto_test.dart
|
||||
test/create_rule_dto_test.dart
|
||||
test/create_tag_dto_test.dart
|
||||
test/create_user_dto_test.dart
|
||||
test/curated_locations_response_dto_test.dart
|
||||
|
@ -332,6 +343,9 @@ test/person_api_test.dart
|
|||
test/person_response_dto_test.dart
|
||||
test/person_update_dto_test.dart
|
||||
test/queue_status_dto_test.dart
|
||||
test/rule_api_test.dart
|
||||
test/rule_key_test.dart
|
||||
test/rule_response_dto_test.dart
|
||||
test/search_album_response_dto_test.dart
|
||||
test/search_api_test.dart
|
||||
test/search_asset_dto_test.dart
|
||||
|
@ -375,6 +389,7 @@ test/transcode_hw_accel_test.dart
|
|||
test/transcode_policy_test.dart
|
||||
test/update_album_dto_test.dart
|
||||
test/update_asset_dto_test.dart
|
||||
test/update_rule_dto_test.dart
|
||||
test/update_tag_dto_test.dart
|
||||
test/update_user_dto_test.dart
|
||||
test/usage_by_user_dto_test.dart
|
||||
|
|
8
mobile/openapi/README.md
generated
8
mobile/openapi/README.md
generated
|
@ -137,6 +137,10 @@ Class | Method | HTTP request | Description
|
|||
*PersonApi* | [**mergePerson**](doc//PersonApi.md#mergeperson) | **POST** /person/{id}/merge |
|
||||
*PersonApi* | [**updatePeople**](doc//PersonApi.md#updatepeople) | **PUT** /person |
|
||||
*PersonApi* | [**updatePerson**](doc//PersonApi.md#updateperson) | **PUT** /person/{id} |
|
||||
*RuleApi* | [**createRule**](doc//RuleApi.md#createrule) | **POST** /rule |
|
||||
*RuleApi* | [**getRule**](doc//RuleApi.md#getrule) | **GET** /rule/{id} |
|
||||
*RuleApi* | [**removeRule**](doc//RuleApi.md#removerule) | **DELETE** /rule/{id} |
|
||||
*RuleApi* | [**updateRule**](doc//RuleApi.md#updaterule) | **PUT** /rule/{id} |
|
||||
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
|
||||
*SearchApi* | [**getSearchConfig**](doc//SearchApi.md#getsearchconfig) | **GET** /search/config |
|
||||
*SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search |
|
||||
|
@ -210,6 +214,7 @@ Class | Method | HTTP request | Description
|
|||
- [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md)
|
||||
- [CreateAlbumDto](doc//CreateAlbumDto.md)
|
||||
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
|
||||
- [CreateRuleDto](doc//CreateRuleDto.md)
|
||||
- [CreateTagDto](doc//CreateTagDto.md)
|
||||
- [CreateUserDto](doc//CreateUserDto.md)
|
||||
- [CuratedLocationsResponseDto](doc//CuratedLocationsResponseDto.md)
|
||||
|
@ -243,6 +248,8 @@ Class | Method | HTTP request | Description
|
|||
- [PersonResponseDto](doc//PersonResponseDto.md)
|
||||
- [PersonUpdateDto](doc//PersonUpdateDto.md)
|
||||
- [QueueStatusDto](doc//QueueStatusDto.md)
|
||||
- [RuleKey](doc//RuleKey.md)
|
||||
- [RuleResponseDto](doc//RuleResponseDto.md)
|
||||
- [SearchAlbumResponseDto](doc//SearchAlbumResponseDto.md)
|
||||
- [SearchAssetDto](doc//SearchAssetDto.md)
|
||||
- [SearchAssetResponseDto](doc//SearchAssetResponseDto.md)
|
||||
|
@ -281,6 +288,7 @@ Class | Method | HTTP request | Description
|
|||
- [TranscodePolicy](doc//TranscodePolicy.md)
|
||||
- [UpdateAlbumDto](doc//UpdateAlbumDto.md)
|
||||
- [UpdateAssetDto](doc//UpdateAssetDto.md)
|
||||
- [UpdateRuleDto](doc//UpdateRuleDto.md)
|
||||
- [UpdateTagDto](doc//UpdateTagDto.md)
|
||||
- [UpdateUserDto](doc//UpdateUserDto.md)
|
||||
- [UsageByUserDto](doc//UsageByUserDto.md)
|
||||
|
|
1
mobile/openapi/doc/AlbumResponseDto.md
generated
1
mobile/openapi/doc/AlbumResponseDto.md
generated
|
@ -20,6 +20,7 @@ Name | Type | Description | Notes
|
|||
**lastModifiedAssetTimestamp** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**owner** | [**UserResponseDto**](UserResponseDto.md) | |
|
||||
**ownerId** | **String** | |
|
||||
**rules** | [**List<RuleResponseDto>**](RuleResponseDto.md) | | [default to const []]
|
||||
**shared** | **bool** | |
|
||||
**sharedUsers** | [**List<UserResponseDto>**](UserResponseDto.md) | | [default to const []]
|
||||
**startDate** | [**DateTime**](DateTime.md) | | [optional]
|
||||
|
|
1
mobile/openapi/doc/AllJobStatusResponseDto.md
generated
1
mobile/openapi/doc/AllJobStatusResponseDto.md
generated
|
@ -15,6 +15,7 @@ Name | Type | Description | Notes
|
|||
**recognizeFaces** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**search** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**sidecar** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**smartAlbum** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**storageTemplateMigration** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**thumbnailGeneration** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**videoConversion** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
|
|
17
mobile/openapi/doc/CreateRuleDto.md
generated
Normal file
17
mobile/openapi/doc/CreateRuleDto.md
generated
Normal file
|
@ -0,0 +1,17 @@
|
|||
# openapi.model.CreateRuleDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**albumId** | **String** | |
|
||||
**key** | [**RuleKey**](RuleKey.md) | |
|
||||
**value** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
238
mobile/openapi/doc/RuleApi.md
generated
Normal file
238
mobile/openapi/doc/RuleApi.md
generated
Normal file
|
@ -0,0 +1,238 @@
|
|||
# openapi.api.RuleApi
|
||||
|
||||
## Load the API package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
All URIs are relative to */api*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**createRule**](RuleApi.md#createrule) | **POST** /rule |
|
||||
[**getRule**](RuleApi.md#getrule) | **GET** /rule/{id} |
|
||||
[**removeRule**](RuleApi.md#removerule) | **DELETE** /rule/{id} |
|
||||
[**updateRule**](RuleApi.md#updaterule) | **PUT** /rule/{id} |
|
||||
|
||||
|
||||
# **createRule**
|
||||
> RuleResponseDto createRule(createRuleDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = RuleApi();
|
||||
final createRuleDto = CreateRuleDto(); // CreateRuleDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.createRule(createRuleDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling RuleApi->createRule: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**createRuleDto** | [**CreateRuleDto**](CreateRuleDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**RuleResponseDto**](RuleResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **getRule**
|
||||
> RuleResponseDto getRule(id)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = RuleApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getRule(id);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling RuleApi->getRule: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**RuleResponseDto**](RuleResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **removeRule**
|
||||
> removeRule(id)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = RuleApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
api_instance.removeRule(id);
|
||||
} catch (e) {
|
||||
print('Exception when calling RuleApi->removeRule: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
|
||||
### Return type
|
||||
|
||||
void (empty response body)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: Not defined
|
||||
|
||||
[[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)
|
||||
|
||||
# **updateRule**
|
||||
> RuleResponseDto updateRule(id, updateRuleDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = RuleApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final updateRuleDto = UpdateRuleDto(); // UpdateRuleDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.updateRule(id, updateRuleDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling RuleApi->updateRule: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**updateRuleDto** | [**UpdateRuleDto**](UpdateRuleDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**RuleResponseDto**](RuleResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
14
mobile/openapi/doc/RuleKey.md
generated
Normal file
14
mobile/openapi/doc/RuleKey.md
generated
Normal file
|
@ -0,0 +1,14 @@
|
|||
# openapi.model.RuleKey
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
18
mobile/openapi/doc/RuleResponseDto.md
generated
Normal file
18
mobile/openapi/doc/RuleResponseDto.md
generated
Normal file
|
@ -0,0 +1,18 @@
|
|||
# openapi.model.RuleResponseDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **String** | |
|
||||
**key** | [**RuleKey**](RuleKey.md) | |
|
||||
**ownerId** | **String** | |
|
||||
**value** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
1
mobile/openapi/doc/SystemConfigJobDto.md
generated
1
mobile/openapi/doc/SystemConfigJobDto.md
generated
|
@ -15,6 +15,7 @@ Name | Type | Description | Notes
|
|||
**recognizeFaces** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**search** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**sidecar** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**smartAlbum** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**storageTemplateMigration** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**thumbnailGeneration** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**videoConversion** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
|
|
16
mobile/openapi/doc/UpdateRuleDto.md
generated
Normal file
16
mobile/openapi/doc/UpdateRuleDto.md
generated
Normal file
|
@ -0,0 +1,16 @@
|
|||
# openapi.model.UpdateRuleDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**key** | [**RuleKey**](RuleKey.md) | |
|
||||
**value** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
5
mobile/openapi/lib/api.dart
generated
5
mobile/openapi/lib/api.dart
generated
|
@ -36,6 +36,7 @@ part 'api/job_api.dart';
|
|||
part 'api/o_auth_api.dart';
|
||||
part 'api/partner_api.dart';
|
||||
part 'api/person_api.dart';
|
||||
part 'api/rule_api.dart';
|
||||
part 'api/search_api.dart';
|
||||
part 'api/server_info_api.dart';
|
||||
part 'api/shared_link_api.dart';
|
||||
|
@ -74,6 +75,7 @@ part 'model/check_existing_assets_dto.dart';
|
|||
part 'model/check_existing_assets_response_dto.dart';
|
||||
part 'model/create_album_dto.dart';
|
||||
part 'model/create_profile_image_response_dto.dart';
|
||||
part 'model/create_rule_dto.dart';
|
||||
part 'model/create_tag_dto.dart';
|
||||
part 'model/create_user_dto.dart';
|
||||
part 'model/curated_locations_response_dto.dart';
|
||||
|
@ -107,6 +109,8 @@ part 'model/people_update_item.dart';
|
|||
part 'model/person_response_dto.dart';
|
||||
part 'model/person_update_dto.dart';
|
||||
part 'model/queue_status_dto.dart';
|
||||
part 'model/rule_key.dart';
|
||||
part 'model/rule_response_dto.dart';
|
||||
part 'model/search_album_response_dto.dart';
|
||||
part 'model/search_asset_dto.dart';
|
||||
part 'model/search_asset_response_dto.dart';
|
||||
|
@ -145,6 +149,7 @@ part 'model/transcode_hw_accel.dart';
|
|||
part 'model/transcode_policy.dart';
|
||||
part 'model/update_album_dto.dart';
|
||||
part 'model/update_asset_dto.dart';
|
||||
part 'model/update_rule_dto.dart';
|
||||
part 'model/update_tag_dto.dart';
|
||||
part 'model/update_user_dto.dart';
|
||||
part 'model/usage_by_user_dto.dart';
|
||||
|
|
205
mobile/openapi/lib/api/rule_api.dart
generated
Normal file
205
mobile/openapi/lib/api/rule_api.dart
generated
Normal file
|
@ -0,0 +1,205 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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 RuleApi {
|
||||
RuleApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'POST /rule' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateRuleDto] createRuleDto (required):
|
||||
Future<Response> createRuleWithHttpInfo(CreateRuleDto createRuleDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/rule';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = createRuleDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'POST',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateRuleDto] createRuleDto (required):
|
||||
Future<RuleResponseDto?> createRule(CreateRuleDto createRuleDto,) async {
|
||||
final response = await createRuleWithHttpInfo(createRuleDto,);
|
||||
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) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'RuleResponseDto',) as RuleResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /rule/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> getRuleWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/rule/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<RuleResponseDto?> getRule(String id,) async {
|
||||
final response = await getRuleWithHttpInfo(id,);
|
||||
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) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'RuleResponseDto',) as RuleResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /rule/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> removeRuleWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/rule/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<void> removeRule(String id,) async {
|
||||
final response = await removeRuleWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PUT /rule/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateRuleDto] updateRuleDto (required):
|
||||
Future<Response> updateRuleWithHttpInfo(String id, UpdateRuleDto updateRuleDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/rule/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = updateRuleDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PUT',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateRuleDto] updateRuleDto (required):
|
||||
Future<RuleResponseDto?> updateRule(String id, UpdateRuleDto updateRuleDto,) async {
|
||||
final response = await updateRuleWithHttpInfo(id, updateRuleDto,);
|
||||
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) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'RuleResponseDto',) as RuleResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
8
mobile/openapi/lib/api_client.dart
generated
8
mobile/openapi/lib/api_client.dart
generated
|
@ -243,6 +243,8 @@ class ApiClient {
|
|||
return CreateAlbumDto.fromJson(value);
|
||||
case 'CreateProfileImageResponseDto':
|
||||
return CreateProfileImageResponseDto.fromJson(value);
|
||||
case 'CreateRuleDto':
|
||||
return CreateRuleDto.fromJson(value);
|
||||
case 'CreateTagDto':
|
||||
return CreateTagDto.fromJson(value);
|
||||
case 'CreateUserDto':
|
||||
|
@ -309,6 +311,10 @@ class ApiClient {
|
|||
return PersonUpdateDto.fromJson(value);
|
||||
case 'QueueStatusDto':
|
||||
return QueueStatusDto.fromJson(value);
|
||||
case 'RuleKey':
|
||||
return RuleKeyTypeTransformer().decode(value);
|
||||
case 'RuleResponseDto':
|
||||
return RuleResponseDto.fromJson(value);
|
||||
case 'SearchAlbumResponseDto':
|
||||
return SearchAlbumResponseDto.fromJson(value);
|
||||
case 'SearchAssetDto':
|
||||
|
@ -385,6 +391,8 @@ class ApiClient {
|
|||
return UpdateAlbumDto.fromJson(value);
|
||||
case 'UpdateAssetDto':
|
||||
return UpdateAssetDto.fromJson(value);
|
||||
case 'UpdateRuleDto':
|
||||
return UpdateRuleDto.fromJson(value);
|
||||
case 'UpdateTagDto':
|
||||
return UpdateTagDto.fromJson(value);
|
||||
case 'UpdateUserDto':
|
||||
|
|
3
mobile/openapi/lib/api_helper.dart
generated
3
mobile/openapi/lib/api_helper.dart
generated
|
@ -70,6 +70,9 @@ String parameterToString(dynamic value) {
|
|||
if (value is JobName) {
|
||||
return JobNameTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is RuleKey) {
|
||||
return RuleKeyTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is SharedLinkType) {
|
||||
return SharedLinkTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
|
10
mobile/openapi/lib/model/album_response_dto.dart
generated
10
mobile/openapi/lib/model/album_response_dto.dart
generated
|
@ -25,6 +25,7 @@ class AlbumResponseDto {
|
|||
this.lastModifiedAssetTimestamp,
|
||||
required this.owner,
|
||||
required this.ownerId,
|
||||
this.rules = const [],
|
||||
required this.shared,
|
||||
this.sharedUsers = const [],
|
||||
this.startDate,
|
||||
|
@ -67,6 +68,8 @@ class AlbumResponseDto {
|
|||
|
||||
String ownerId;
|
||||
|
||||
List<RuleResponseDto> rules;
|
||||
|
||||
bool shared;
|
||||
|
||||
List<UserResponseDto> sharedUsers;
|
||||
|
@ -95,6 +98,7 @@ class AlbumResponseDto {
|
|||
other.lastModifiedAssetTimestamp == lastModifiedAssetTimestamp &&
|
||||
other.owner == owner &&
|
||||
other.ownerId == ownerId &&
|
||||
other.rules == rules &&
|
||||
other.shared == shared &&
|
||||
other.sharedUsers == sharedUsers &&
|
||||
other.startDate == startDate &&
|
||||
|
@ -115,13 +119,14 @@ class AlbumResponseDto {
|
|||
(lastModifiedAssetTimestamp == null ? 0 : lastModifiedAssetTimestamp!.hashCode) +
|
||||
(owner.hashCode) +
|
||||
(ownerId.hashCode) +
|
||||
(rules.hashCode) +
|
||||
(shared.hashCode) +
|
||||
(sharedUsers.hashCode) +
|
||||
(startDate == null ? 0 : startDate!.hashCode) +
|
||||
(updatedAt.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, assetCount=$assetCount, assets=$assets, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, owner=$owner, ownerId=$ownerId, shared=$shared, sharedUsers=$sharedUsers, startDate=$startDate, updatedAt=$updatedAt]';
|
||||
String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, assetCount=$assetCount, assets=$assets, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, owner=$owner, ownerId=$ownerId, rules=$rules, shared=$shared, sharedUsers=$sharedUsers, startDate=$startDate, updatedAt=$updatedAt]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -149,6 +154,7 @@ class AlbumResponseDto {
|
|||
}
|
||||
json[r'owner'] = this.owner;
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
json[r'rules'] = this.rules;
|
||||
json[r'shared'] = this.shared;
|
||||
json[r'sharedUsers'] = this.sharedUsers;
|
||||
if (this.startDate != null) {
|
||||
|
@ -180,6 +186,7 @@ class AlbumResponseDto {
|
|||
lastModifiedAssetTimestamp: mapDateTime(json, r'lastModifiedAssetTimestamp', ''),
|
||||
owner: UserResponseDto.fromJson(json[r'owner'])!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
rules: RuleResponseDto.listFromJson(json[r'rules']),
|
||||
shared: mapValueOfType<bool>(json, r'shared')!,
|
||||
sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers']),
|
||||
startDate: mapDateTime(json, r'startDate', ''),
|
||||
|
@ -241,6 +248,7 @@ class AlbumResponseDto {
|
|||
'id',
|
||||
'owner',
|
||||
'ownerId',
|
||||
'rules',
|
||||
'shared',
|
||||
'sharedUsers',
|
||||
'updatedAt',
|
||||
|
|
|
@ -20,6 +20,7 @@ class AllJobStatusResponseDto {
|
|||
required this.recognizeFaces,
|
||||
required this.search,
|
||||
required this.sidecar,
|
||||
required this.smartAlbum,
|
||||
required this.storageTemplateMigration,
|
||||
required this.thumbnailGeneration,
|
||||
required this.videoConversion,
|
||||
|
@ -39,6 +40,8 @@ class AllJobStatusResponseDto {
|
|||
|
||||
JobStatusDto sidecar;
|
||||
|
||||
JobStatusDto smartAlbum;
|
||||
|
||||
JobStatusDto storageTemplateMigration;
|
||||
|
||||
JobStatusDto thumbnailGeneration;
|
||||
|
@ -54,6 +57,7 @@ class AllJobStatusResponseDto {
|
|||
other.recognizeFaces == recognizeFaces &&
|
||||
other.search == search &&
|
||||
other.sidecar == sidecar &&
|
||||
other.smartAlbum == smartAlbum &&
|
||||
other.storageTemplateMigration == storageTemplateMigration &&
|
||||
other.thumbnailGeneration == thumbnailGeneration &&
|
||||
other.videoConversion == videoConversion;
|
||||
|
@ -68,12 +72,13 @@ class AllJobStatusResponseDto {
|
|||
(recognizeFaces.hashCode) +
|
||||
(search.hashCode) +
|
||||
(sidecar.hashCode) +
|
||||
(smartAlbum.hashCode) +
|
||||
(storageTemplateMigration.hashCode) +
|
||||
(thumbnailGeneration.hashCode) +
|
||||
(videoConversion.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, clipEncoding=$clipEncoding, metadataExtraction=$metadataExtraction, objectTagging=$objectTagging, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
String toString() => 'AllJobStatusResponseDto[backgroundTask=$backgroundTask, clipEncoding=$clipEncoding, metadataExtraction=$metadataExtraction, objectTagging=$objectTagging, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, smartAlbum=$smartAlbum, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -84,6 +89,7 @@ class AllJobStatusResponseDto {
|
|||
json[r'recognizeFaces'] = this.recognizeFaces;
|
||||
json[r'search'] = this.search;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
json[r'smartAlbum'] = this.smartAlbum;
|
||||
json[r'storageTemplateMigration'] = this.storageTemplateMigration;
|
||||
json[r'thumbnailGeneration'] = this.thumbnailGeneration;
|
||||
json[r'videoConversion'] = this.videoConversion;
|
||||
|
@ -105,6 +111,7 @@ class AllJobStatusResponseDto {
|
|||
recognizeFaces: JobStatusDto.fromJson(json[r'recognizeFaces'])!,
|
||||
search: JobStatusDto.fromJson(json[r'search'])!,
|
||||
sidecar: JobStatusDto.fromJson(json[r'sidecar'])!,
|
||||
smartAlbum: JobStatusDto.fromJson(json[r'smartAlbum'])!,
|
||||
storageTemplateMigration: JobStatusDto.fromJson(json[r'storageTemplateMigration'])!,
|
||||
thumbnailGeneration: JobStatusDto.fromJson(json[r'thumbnailGeneration'])!,
|
||||
videoConversion: JobStatusDto.fromJson(json[r'videoConversion'])!,
|
||||
|
@ -162,6 +169,7 @@ class AllJobStatusResponseDto {
|
|||
'recognizeFaces',
|
||||
'search',
|
||||
'sidecar',
|
||||
'smartAlbum',
|
||||
'storageTemplateMigration',
|
||||
'thumbnailGeneration',
|
||||
'videoConversion',
|
||||
|
|
114
mobile/openapi/lib/model/create_rule_dto.dart
generated
Normal file
114
mobile/openapi/lib/model/create_rule_dto.dart
generated
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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 CreateRuleDto {
|
||||
/// Returns a new [CreateRuleDto] instance.
|
||||
CreateRuleDto({
|
||||
required this.albumId,
|
||||
required this.key,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
String albumId;
|
||||
|
||||
RuleKey key;
|
||||
|
||||
String value;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CreateRuleDto &&
|
||||
other.albumId == albumId &&
|
||||
other.key == key &&
|
||||
other.value == value;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(albumId.hashCode) +
|
||||
(key.hashCode) +
|
||||
(value.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CreateRuleDto[albumId=$albumId, key=$key, value=$value]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'albumId'] = this.albumId;
|
||||
json[r'key'] = this.key;
|
||||
json[r'value'] = this.value;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [CreateRuleDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static CreateRuleDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return CreateRuleDto(
|
||||
albumId: mapValueOfType<String>(json, r'albumId')!,
|
||||
key: RuleKey.fromJson(json[r'key'])!,
|
||||
value: mapValueOfType<String>(json, r'value')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<CreateRuleDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <CreateRuleDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = CreateRuleDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, CreateRuleDto> mapFromJson(dynamic json) {
|
||||
final map = <String, CreateRuleDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = CreateRuleDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of CreateRuleDto-objects as value to a dart map
|
||||
static Map<String, List<CreateRuleDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<CreateRuleDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = CreateRuleDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'albumId',
|
||||
'key',
|
||||
'value',
|
||||
};
|
||||
}
|
||||
|
3
mobile/openapi/lib/model/job_name.dart
generated
3
mobile/openapi/lib/model/job_name.dart
generated
|
@ -33,6 +33,7 @@ class JobName {
|
|||
static const storageTemplateMigration = JobName._(r'storageTemplateMigration');
|
||||
static const search = JobName._(r'search');
|
||||
static const sidecar = JobName._(r'sidecar');
|
||||
static const smartAlbum = JobName._(r'smartAlbum');
|
||||
|
||||
/// List of all possible values in this [enum][JobName].
|
||||
static const values = <JobName>[
|
||||
|
@ -46,6 +47,7 @@ class JobName {
|
|||
storageTemplateMigration,
|
||||
search,
|
||||
sidecar,
|
||||
smartAlbum,
|
||||
];
|
||||
|
||||
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
|
||||
|
@ -94,6 +96,7 @@ class JobNameTypeTransformer {
|
|||
case r'storageTemplateMigration': return JobName.storageTemplateMigration;
|
||||
case r'search': return JobName.search;
|
||||
case r'sidecar': return JobName.sidecar;
|
||||
case r'smartAlbum': return JobName.smartAlbum;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
|
103
mobile/openapi/lib/model/rule_key.dart
generated
Normal file
103
mobile/openapi/lib/model/rule_key.dart
generated
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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 RuleKey {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const RuleKey._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const person = RuleKey._(r'person');
|
||||
static const takenAfter = RuleKey._(r'taken-after');
|
||||
static const city = RuleKey._(r'city');
|
||||
static const state = RuleKey._(r'state');
|
||||
static const country = RuleKey._(r'country');
|
||||
static const cameraMake = RuleKey._(r'camera-make');
|
||||
static const cameraModel = RuleKey._(r'camera-model');
|
||||
static const location = RuleKey._(r'location');
|
||||
|
||||
/// List of all possible values in this [enum][RuleKey].
|
||||
static const values = <RuleKey>[
|
||||
person,
|
||||
takenAfter,
|
||||
city,
|
||||
state,
|
||||
country,
|
||||
cameraMake,
|
||||
cameraModel,
|
||||
location,
|
||||
];
|
||||
|
||||
static RuleKey? fromJson(dynamic value) => RuleKeyTypeTransformer().decode(value);
|
||||
|
||||
static List<RuleKey>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <RuleKey>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = RuleKey.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [RuleKey] to String,
|
||||
/// and [decode] dynamic data back to [RuleKey].
|
||||
class RuleKeyTypeTransformer {
|
||||
factory RuleKeyTypeTransformer() => _instance ??= const RuleKeyTypeTransformer._();
|
||||
|
||||
const RuleKeyTypeTransformer._();
|
||||
|
||||
String encode(RuleKey data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a RuleKey.
|
||||
///
|
||||
/// 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.
|
||||
RuleKey? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'person': return RuleKey.person;
|
||||
case r'taken-after': return RuleKey.takenAfter;
|
||||
case r'city': return RuleKey.city;
|
||||
case r'state': return RuleKey.state;
|
||||
case r'country': return RuleKey.country;
|
||||
case r'camera-make': return RuleKey.cameraMake;
|
||||
case r'camera-model': return RuleKey.cameraModel;
|
||||
case r'location': return RuleKey.location;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [RuleKeyTypeTransformer] instance.
|
||||
static RuleKeyTypeTransformer? _instance;
|
||||
}
|
||||
|
122
mobile/openapi/lib/model/rule_response_dto.dart
generated
Normal file
122
mobile/openapi/lib/model/rule_response_dto.dart
generated
Normal file
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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 RuleResponseDto {
|
||||
/// Returns a new [RuleResponseDto] instance.
|
||||
RuleResponseDto({
|
||||
required this.id,
|
||||
required this.key,
|
||||
required this.ownerId,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
String id;
|
||||
|
||||
RuleKey key;
|
||||
|
||||
String ownerId;
|
||||
|
||||
String value;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is RuleResponseDto &&
|
||||
other.id == id &&
|
||||
other.key == key &&
|
||||
other.ownerId == ownerId &&
|
||||
other.value == value;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(id.hashCode) +
|
||||
(key.hashCode) +
|
||||
(ownerId.hashCode) +
|
||||
(value.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'RuleResponseDto[id=$id, key=$key, ownerId=$ownerId, value=$value]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'id'] = this.id;
|
||||
json[r'key'] = this.key;
|
||||
json[r'ownerId'] = this.ownerId;
|
||||
json[r'value'] = this.value;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [RuleResponseDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static RuleResponseDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return RuleResponseDto(
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
key: RuleKey.fromJson(json[r'key'])!,
|
||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||
value: mapValueOfType<String>(json, r'value')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<RuleResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <RuleResponseDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = RuleResponseDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, RuleResponseDto> mapFromJson(dynamic json) {
|
||||
final map = <String, RuleResponseDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = RuleResponseDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of RuleResponseDto-objects as value to a dart map
|
||||
static Map<String, List<RuleResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<RuleResponseDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = RuleResponseDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'id',
|
||||
'key',
|
||||
'ownerId',
|
||||
'value',
|
||||
};
|
||||
}
|
||||
|
10
mobile/openapi/lib/model/system_config_job_dto.dart
generated
10
mobile/openapi/lib/model/system_config_job_dto.dart
generated
|
@ -20,6 +20,7 @@ class SystemConfigJobDto {
|
|||
required this.recognizeFaces,
|
||||
required this.search,
|
||||
required this.sidecar,
|
||||
required this.smartAlbum,
|
||||
required this.storageTemplateMigration,
|
||||
required this.thumbnailGeneration,
|
||||
required this.videoConversion,
|
||||
|
@ -39,6 +40,8 @@ class SystemConfigJobDto {
|
|||
|
||||
JobSettingsDto sidecar;
|
||||
|
||||
JobSettingsDto smartAlbum;
|
||||
|
||||
JobSettingsDto storageTemplateMigration;
|
||||
|
||||
JobSettingsDto thumbnailGeneration;
|
||||
|
@ -54,6 +57,7 @@ class SystemConfigJobDto {
|
|||
other.recognizeFaces == recognizeFaces &&
|
||||
other.search == search &&
|
||||
other.sidecar == sidecar &&
|
||||
other.smartAlbum == smartAlbum &&
|
||||
other.storageTemplateMigration == storageTemplateMigration &&
|
||||
other.thumbnailGeneration == thumbnailGeneration &&
|
||||
other.videoConversion == videoConversion;
|
||||
|
@ -68,12 +72,13 @@ class SystemConfigJobDto {
|
|||
(recognizeFaces.hashCode) +
|
||||
(search.hashCode) +
|
||||
(sidecar.hashCode) +
|
||||
(smartAlbum.hashCode) +
|
||||
(storageTemplateMigration.hashCode) +
|
||||
(thumbnailGeneration.hashCode) +
|
||||
(videoConversion.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigJobDto[backgroundTask=$backgroundTask, clipEncoding=$clipEncoding, metadataExtraction=$metadataExtraction, objectTagging=$objectTagging, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
String toString() => 'SystemConfigJobDto[backgroundTask=$backgroundTask, clipEncoding=$clipEncoding, metadataExtraction=$metadataExtraction, objectTagging=$objectTagging, recognizeFaces=$recognizeFaces, search=$search, sidecar=$sidecar, smartAlbum=$smartAlbum, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
|
@ -84,6 +89,7 @@ class SystemConfigJobDto {
|
|||
json[r'recognizeFaces'] = this.recognizeFaces;
|
||||
json[r'search'] = this.search;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
json[r'smartAlbum'] = this.smartAlbum;
|
||||
json[r'storageTemplateMigration'] = this.storageTemplateMigration;
|
||||
json[r'thumbnailGeneration'] = this.thumbnailGeneration;
|
||||
json[r'videoConversion'] = this.videoConversion;
|
||||
|
@ -105,6 +111,7 @@ class SystemConfigJobDto {
|
|||
recognizeFaces: JobSettingsDto.fromJson(json[r'recognizeFaces'])!,
|
||||
search: JobSettingsDto.fromJson(json[r'search'])!,
|
||||
sidecar: JobSettingsDto.fromJson(json[r'sidecar'])!,
|
||||
smartAlbum: JobSettingsDto.fromJson(json[r'smartAlbum'])!,
|
||||
storageTemplateMigration: JobSettingsDto.fromJson(json[r'storageTemplateMigration'])!,
|
||||
thumbnailGeneration: JobSettingsDto.fromJson(json[r'thumbnailGeneration'])!,
|
||||
videoConversion: JobSettingsDto.fromJson(json[r'videoConversion'])!,
|
||||
|
@ -162,6 +169,7 @@ class SystemConfigJobDto {
|
|||
'recognizeFaces',
|
||||
'search',
|
||||
'sidecar',
|
||||
'smartAlbum',
|
||||
'storageTemplateMigration',
|
||||
'thumbnailGeneration',
|
||||
'videoConversion',
|
||||
|
|
106
mobile/openapi/lib/model/update_rule_dto.dart
generated
Normal file
106
mobile/openapi/lib/model/update_rule_dto.dart
generated
Normal file
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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 UpdateRuleDto {
|
||||
/// Returns a new [UpdateRuleDto] instance.
|
||||
UpdateRuleDto({
|
||||
required this.key,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
RuleKey key;
|
||||
|
||||
String value;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is UpdateRuleDto &&
|
||||
other.key == key &&
|
||||
other.value == value;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(key.hashCode) +
|
||||
(value.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'UpdateRuleDto[key=$key, value=$value]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'key'] = this.key;
|
||||
json[r'value'] = this.value;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [UpdateRuleDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static UpdateRuleDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return UpdateRuleDto(
|
||||
key: RuleKey.fromJson(json[r'key'])!,
|
||||
value: mapValueOfType<String>(json, r'value')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<UpdateRuleDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <UpdateRuleDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = UpdateRuleDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, UpdateRuleDto> mapFromJson(dynamic json) {
|
||||
final map = <String, UpdateRuleDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = UpdateRuleDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of UpdateRuleDto-objects as value to a dart map
|
||||
static Map<String, List<UpdateRuleDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<UpdateRuleDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = UpdateRuleDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'key',
|
||||
'value',
|
||||
};
|
||||
}
|
||||
|
5
mobile/openapi/test/album_response_dto_test.dart
generated
5
mobile/openapi/test/album_response_dto_test.dart
generated
|
@ -76,6 +76,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// List<RuleResponseDto> rules (default value: const [])
|
||||
test('to test the property `rules`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool shared
|
||||
test('to test the property `shared`', () async {
|
||||
// TODO
|
||||
|
|
|
@ -51,6 +51,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto smartAlbum
|
||||
test('to test the property `smartAlbum`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto storageTemplateMigration
|
||||
test('to test the property `storageTemplateMigration`', () async {
|
||||
// TODO
|
||||
|
|
37
mobile/openapi/test/create_rule_dto_test.dart
generated
Normal file
37
mobile/openapi/test/create_rule_dto_test.dart
generated
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for CreateRuleDto
|
||||
void main() {
|
||||
// final instance = CreateRuleDto();
|
||||
|
||||
group('test CreateRuleDto', () {
|
||||
// String albumId
|
||||
test('to test the property `albumId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// RuleKey key
|
||||
test('to test the property `key`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String value
|
||||
test('to test the property `value`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
41
mobile/openapi/test/rule_api_test.dart
generated
Normal file
41
mobile/openapi/test/rule_api_test.dart
generated
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
||||
/// tests for RuleApi
|
||||
void main() {
|
||||
// final instance = RuleApi();
|
||||
|
||||
group('tests for RuleApi', () {
|
||||
//Future<RuleResponseDto> createRule(CreateRuleDto createRuleDto) async
|
||||
test('test createRule', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<RuleResponseDto> getRule(String id) async
|
||||
test('test getRule', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future removeRule(String id) async
|
||||
test('test removeRule', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<RuleResponseDto> updateRule(String id, UpdateRuleDto updateRuleDto) async
|
||||
test('test updateRule', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
}
|
21
mobile/openapi/test/rule_key_test.dart
generated
Normal file
21
mobile/openapi/test/rule_key_test.dart
generated
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for RuleKey
|
||||
void main() {
|
||||
|
||||
group('test RuleKey', () {
|
||||
|
||||
});
|
||||
|
||||
}
|
42
mobile/openapi/test/rule_response_dto_test.dart
generated
Normal file
42
mobile/openapi/test/rule_response_dto_test.dart
generated
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for RuleResponseDto
|
||||
void main() {
|
||||
// final instance = RuleResponseDto();
|
||||
|
||||
group('test RuleResponseDto', () {
|
||||
// String id
|
||||
test('to test the property `id`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// RuleKey key
|
||||
test('to test the property `key`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String ownerId
|
||||
test('to test the property `ownerId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String value
|
||||
test('to test the property `value`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -51,6 +51,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto smartAlbum
|
||||
test('to test the property `smartAlbum`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto storageTemplateMigration
|
||||
test('to test the property `storageTemplateMigration`', () async {
|
||||
// TODO
|
||||
|
|
32
mobile/openapi/test/update_rule_dto_test.dart
generated
Normal file
32
mobile/openapi/test/update_rule_dto_test.dart
generated
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// 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
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for UpdateRuleDto
|
||||
void main() {
|
||||
// final instance = UpdateRuleDto();
|
||||
|
||||
group('test UpdateRuleDto', () {
|
||||
// RuleKey key
|
||||
test('to test the property `key`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String value
|
||||
test('to test the property `value`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -2976,6 +2976,173 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/rule": {
|
||||
"post": {
|
||||
"operationId": "createRule",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateRuleDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RuleResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Rule"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/rule/{id}": {
|
||||
"delete": {
|
||||
"operationId": "removeRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Rule"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "getRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RuleResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Rule"
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "updateRule",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateRuleDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RuleResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Rule"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/search": {
|
||||
"get": {
|
||||
"operationId": "search",
|
||||
|
@ -4792,6 +4959,12 @@
|
|||
"ownerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"rules": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/RuleResponseDto"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"shared": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -4823,7 +4996,8 @@
|
|||
"sharedUsers",
|
||||
"hasSharedLink",
|
||||
"assets",
|
||||
"owner"
|
||||
"owner",
|
||||
"rules"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
@ -4850,6 +5024,9 @@
|
|||
"sidecar": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
"smartAlbum": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
"storageTemplateMigration": {
|
||||
"$ref": "#/components/schemas/JobStatusDto"
|
||||
},
|
||||
|
@ -4870,7 +5047,8 @@
|
|||
"backgroundTask",
|
||||
"search",
|
||||
"recognizeFaces",
|
||||
"sidecar"
|
||||
"sidecar",
|
||||
"smartAlbum"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
@ -5414,6 +5592,26 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CreateRuleDto": {
|
||||
"properties": {
|
||||
"albumId": {
|
||||
"format": "uuid",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/components/schemas/RuleKey"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"value",
|
||||
"albumId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"CreateTagDto": {
|
||||
"properties": {
|
||||
"name": {
|
||||
|
@ -5846,7 +6044,8 @@
|
|||
"backgroundTask",
|
||||
"storageTemplateMigration",
|
||||
"search",
|
||||
"sidecar"
|
||||
"sidecar",
|
||||
"smartAlbum"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -6170,6 +6369,42 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"RuleKey": {
|
||||
"enum": [
|
||||
"person",
|
||||
"taken-after",
|
||||
"city",
|
||||
"state",
|
||||
"country",
|
||||
"camera-make",
|
||||
"camera-model",
|
||||
"location"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"RuleResponseDto": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/components/schemas/RuleKey"
|
||||
},
|
||||
"ownerId": {
|
||||
"type": "string"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"value",
|
||||
"id",
|
||||
"ownerId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SearchAlbumResponseDto": {
|
||||
"properties": {
|
||||
"count": {
|
||||
|
@ -6753,6 +6988,9 @@
|
|||
"sidecar": {
|
||||
"$ref": "#/components/schemas/JobSettingsDto"
|
||||
},
|
||||
"smartAlbum": {
|
||||
"$ref": "#/components/schemas/JobSettingsDto"
|
||||
},
|
||||
"storageTemplateMigration": {
|
||||
"$ref": "#/components/schemas/JobSettingsDto"
|
||||
},
|
||||
|
@ -6773,7 +7011,8 @@
|
|||
"backgroundTask",
|
||||
"search",
|
||||
"recognizeFaces",
|
||||
"sidecar"
|
||||
"sidecar",
|
||||
"smartAlbum"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
@ -7049,6 +7288,21 @@
|
|||
},
|
||||
"type": "object"
|
||||
},
|
||||
"UpdateRuleDto": {
|
||||
"properties": {
|
||||
"key": {
|
||||
"$ref": "#/components/schemas/RuleKey"
|
||||
},
|
||||
"value": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"key",
|
||||
"value"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"UpdateTagDto": {
|
||||
"properties": {
|
||||
"name": {
|
||||
|
|
|
@ -19,6 +19,11 @@ export enum Permission {
|
|||
ALBUM_SHARE = 'album.share',
|
||||
ALBUM_DOWNLOAD = 'album.download',
|
||||
|
||||
RULE_READ = 'rule.read',
|
||||
RULE_CREATE = 'rule.create',
|
||||
RULE_UPDATE = 'rule.update',
|
||||
RULE_DELETE = 'rule.delete',
|
||||
|
||||
ARCHIVE_READ = 'archive.read',
|
||||
|
||||
LIBRARY_READ = 'library.read',
|
||||
|
@ -158,6 +163,18 @@ export class AccessCore {
|
|||
case Permission.ALBUM_REMOVE_ASSET:
|
||||
return this.repository.album.hasOwnerAccess(authUser.id, id);
|
||||
|
||||
case Permission.RULE_CREATE:
|
||||
// id is albumId here
|
||||
return (
|
||||
(await this.repository.album.hasOwnerAccess(authUser.id, id)) ||
|
||||
(await this.repository.album.hasSharedAlbumAccess(authUser.id, id))
|
||||
);
|
||||
|
||||
case Permission.RULE_READ:
|
||||
case Permission.RULE_UPDATE:
|
||||
case Permission.RULE_DELETE:
|
||||
return this.repository.rule.hasOwnerAccess(authUser.id, id);
|
||||
|
||||
case Permission.ARCHIVE_READ:
|
||||
return authUser.id === id;
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ export interface IAccessRepository {
|
|||
hasSharedLinkAccess(sharedLinkId: string, albumId: string): Promise<boolean>;
|
||||
};
|
||||
|
||||
rule: {
|
||||
hasOwnerAccess(userId: string, ruleId: string): Promise<boolean>;
|
||||
};
|
||||
|
||||
library: {
|
||||
hasPartnerAccess(userId: string, partnerId: string): Promise<boolean>;
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ import { AlbumEntity } from '@app/infra/entities';
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { AssetResponseDto, mapAsset } from '../asset';
|
||||
import { mapUser, UserResponseDto } from '../user';
|
||||
import { mapRule, RuleResponseDto } from './dto';
|
||||
|
||||
export class AlbumResponseDto {
|
||||
id!: string;
|
||||
|
@ -21,16 +22,11 @@ export class AlbumResponseDto {
|
|||
lastModifiedAssetTimestamp?: Date;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
rules!: RuleResponseDto[];
|
||||
}
|
||||
|
||||
export const mapAlbum = (entity: AlbumEntity, withAssets: boolean): AlbumResponseDto => {
|
||||
const sharedUsers: UserResponseDto[] = [];
|
||||
|
||||
entity.sharedUsers?.forEach((user) => {
|
||||
const userDto = mapUser(user);
|
||||
sharedUsers.push(userDto);
|
||||
});
|
||||
|
||||
const sharedUsers: UserResponseDto[] = (entity.sharedUsers || []).map(mapUser);
|
||||
const assets = entity.assets || [];
|
||||
|
||||
const hasSharedLink = entity.sharedLinks?.length > 0;
|
||||
|
@ -52,6 +48,7 @@ export const mapAlbum = (entity: AlbumEntity, withAssets: boolean): AlbumRespons
|
|||
endDate: assets.at(-1)?.fileCreatedAt || undefined,
|
||||
assets: (withAssets ? assets : []).map((asset) => mapAsset(asset)),
|
||||
assetCount: entity.assets?.length || 0,
|
||||
rules: (entity.rules || []).map(mapRule),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -185,6 +185,7 @@ describe(AlbumService.name, () => {
|
|||
endDate: undefined,
|
||||
hasSharedLink: false,
|
||||
updatedAt: expect.anything(),
|
||||
rules: [],
|
||||
});
|
||||
|
||||
expect(jobMock.queue).toHaveBeenCalledWith({
|
||||
|
|
|
@ -3,3 +3,4 @@ export * from './album-create.dto';
|
|||
export * from './album-update.dto';
|
||||
export * from './album.dto';
|
||||
export * from './get-albums.dto';
|
||||
export * from '../../rule/rule.dto';
|
||||
|
|
|
@ -18,6 +18,7 @@ import { StorageTemplateService } from './storage-template';
|
|||
import { INITIAL_SYSTEM_CONFIG, SystemConfigService } from './system-config';
|
||||
import { TagService } from './tag';
|
||||
import { UserService } from './user';
|
||||
import { RuleService } from './rule';
|
||||
|
||||
const providers: Provider[] = [
|
||||
AlbumService,
|
||||
|
@ -30,6 +31,7 @@ const providers: Provider[] = [
|
|||
MetadataService,
|
||||
PersonService,
|
||||
PartnerService,
|
||||
RuleService,
|
||||
SearchService,
|
||||
ServerInfoService,
|
||||
SharedLinkService,
|
||||
|
|
|
@ -15,6 +15,7 @@ export * from './media';
|
|||
export * from './metadata';
|
||||
export * from './partner';
|
||||
export * from './person';
|
||||
export * from './rule';
|
||||
export * from './search';
|
||||
export * from './server-info';
|
||||
export * from './shared-link';
|
||||
|
|
|
@ -9,6 +9,7 @@ export enum QueueName {
|
|||
STORAGE_TEMPLATE_MIGRATION = 'storageTemplateMigration',
|
||||
SEARCH = 'search',
|
||||
SIDECAR = 'sidecar',
|
||||
SMART_ALBUM = 'smartAlbum',
|
||||
}
|
||||
|
||||
export enum JobCommand {
|
||||
|
@ -75,6 +76,9 @@ export enum JobName {
|
|||
QUEUE_SIDECAR = 'queue-sidecar',
|
||||
SIDECAR_DISCOVERY = 'sidecar-discovery',
|
||||
SIDECAR_SYNC = 'sidecar-sync',
|
||||
|
||||
// smart albums
|
||||
SMART_ALBUM_INDEX = 'smart-album-index',
|
||||
}
|
||||
|
||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
|
||||
|
@ -138,4 +142,7 @@ export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
|
|||
[JobName.QUEUE_SIDECAR]: QueueName.SIDECAR,
|
||||
[JobName.SIDECAR_DISCOVERY]: QueueName.SIDECAR,
|
||||
[JobName.SIDECAR_SYNC]: QueueName.SIDECAR,
|
||||
|
||||
// smart albums
|
||||
[JobName.SMART_ALBUM_INDEX]: QueueName.SMART_ALBUM,
|
||||
};
|
||||
|
|
|
@ -78,4 +78,7 @@ export class AllJobStatusResponseDto implements Record<QueueName, JobStatusDto>
|
|||
|
||||
@ApiProperty({ type: JobStatusDto })
|
||||
[QueueName.SIDECAR]!: JobStatusDto;
|
||||
|
||||
@ApiProperty({ type: JobStatusDto })
|
||||
[QueueName.SMART_ALBUM]!: JobStatusDto;
|
||||
}
|
||||
|
|
|
@ -29,3 +29,13 @@ export interface IBulkEntityJob extends IBaseJob {
|
|||
export interface IDeleteFilesJob extends IBaseJob {
|
||||
files: Array<string | null | undefined>;
|
||||
}
|
||||
|
||||
export interface ISmartAlbumIndexJob extends IBaseJob {
|
||||
albumId: string;
|
||||
ruleId: string;
|
||||
}
|
||||
|
||||
export interface ISmartAlbumInsertJob extends IBaseJob {
|
||||
albumId: string;
|
||||
assetId: string;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import {
|
|||
IDeleteFilesJob,
|
||||
IEntityJob,
|
||||
IFaceThumbnailJob,
|
||||
ISmartAlbumIndexJob,
|
||||
ISmartAlbumInsertJob,
|
||||
} from './job.interface';
|
||||
|
||||
export interface JobCounts {
|
||||
|
@ -80,7 +82,10 @@ export type JobItem =
|
|||
| { name: JobName.SEARCH_INDEX_ALBUM; data: IBulkEntityJob }
|
||||
| { name: JobName.SEARCH_REMOVE_ASSET; data: IBulkEntityJob }
|
||||
| { name: JobName.SEARCH_REMOVE_ALBUM; data: IBulkEntityJob }
|
||||
| { name: JobName.SEARCH_REMOVE_FACE; data: IAssetFaceJob };
|
||||
| { name: JobName.SEARCH_REMOVE_FACE; data: IAssetFaceJob }
|
||||
|
||||
// Smart album
|
||||
| { name: JobName.SMART_ALBUM_INDEX; data: ISmartAlbumIndexJob };
|
||||
|
||||
export type JobHandler<T = any> = (data: T) => boolean | Promise<boolean>;
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ describe(JobService.name, () => {
|
|||
[QueueName.THUMBNAIL_GENERATION]: expectedJobStatus,
|
||||
[QueueName.VIDEO_CONVERSION]: expectedJobStatus,
|
||||
[QueueName.RECOGNIZE_FACES]: expectedJobStatus,
|
||||
[QueueName.SMART_ALBUM]: expectedJobStatus,
|
||||
[QueueName.SIDECAR]: expectedJobStatus,
|
||||
});
|
||||
});
|
||||
|
@ -224,6 +225,7 @@ describe(JobService.name, () => {
|
|||
[QueueName.RECOGNIZE_FACES]: { concurrency: 10 },
|
||||
[QueueName.SEARCH]: { concurrency: 10 },
|
||||
[QueueName.SIDECAR]: { concurrency: 10 },
|
||||
[QueueName.SMART_ALBUM]: { concurrency: 10 },
|
||||
[QueueName.STORAGE_TEMPLATE_MIGRATION]: { concurrency: 10 },
|
||||
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 10 },
|
||||
[QueueName.VIDEO_CONVERSION]: { concurrency: 10 },
|
||||
|
@ -236,6 +238,7 @@ describe(JobService.name, () => {
|
|||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.OBJECT_TAGGING, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.RECOGNIZE_FACES, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.SIDECAR, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.SMART_ALBUM, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.STORAGE_TEMPLATE_MIGRATION, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.THUMBNAIL_GENERATION, 10);
|
||||
expect(jobMock.setConcurrency).toHaveBeenCalledWith(QueueName.VIDEO_CONVERSION, 10);
|
||||
|
|
3
server/src/domain/rule/index.ts
Normal file
3
server/src/domain/rule/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export * from './rule.dto';
|
||||
export * from './rule.repository';
|
||||
export * from './rule.service';
|
101
server/src/domain/rule/rule.dto.ts
Normal file
101
server/src/domain/rule/rule.dto.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { GeoRuleValue, RuleEntity, RuleKey, RuleValue, RuleValueType, RULE_TO_TYPE } from '@app/infra/entities';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ClassConstructor, plainToInstance, Transform, Type } from 'class-transformer';
|
||||
import {
|
||||
IsDate,
|
||||
IsEnum,
|
||||
IsLatitude,
|
||||
IsLongitude,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsPositive,
|
||||
IsString,
|
||||
IsUUID,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { ValidateUUID } from '../domain.util';
|
||||
|
||||
class UUIDRuleDto {
|
||||
@IsUUID(4)
|
||||
value!: string;
|
||||
}
|
||||
|
||||
class StringRuleDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
value!: string;
|
||||
}
|
||||
|
||||
class DateRuleDto {
|
||||
@IsDate()
|
||||
@Type(() => Date)
|
||||
value!: Date;
|
||||
}
|
||||
|
||||
class GeoRuleValueDto implements GeoRuleValue {
|
||||
@IsLatitude()
|
||||
lat!: number;
|
||||
|
||||
@IsLongitude()
|
||||
lng!: number;
|
||||
|
||||
@IsPositive()
|
||||
@IsNumber()
|
||||
radius!: number;
|
||||
}
|
||||
|
||||
class GeoRuleDto {
|
||||
@ValidateNested()
|
||||
@Type(() => GeoRuleValueDto)
|
||||
value!: GeoRuleValueDto;
|
||||
}
|
||||
|
||||
const toRuleValueDto = (key: RuleKey, value: RuleValue) => {
|
||||
const type = RULE_TO_TYPE[key];
|
||||
const map: Record<RuleValueType, ClassConstructor<{ value: RuleValue }>> = {
|
||||
[RuleValueType.UUID]: UUIDRuleDto,
|
||||
[RuleValueType.STRING]: StringRuleDto,
|
||||
[RuleValueType.DATE]: DateRuleDto,
|
||||
[RuleValueType.GEO]: GeoRuleDto,
|
||||
};
|
||||
|
||||
if (type && map[type]) {
|
||||
return plainToInstance(map[type], { value });
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export class UpdateRuleDto {
|
||||
@ApiProperty({ enumName: 'RuleKey', enum: RuleKey })
|
||||
@IsEnum(RuleKey)
|
||||
key!: RuleKey;
|
||||
|
||||
@ApiProperty({ type: String })
|
||||
@ValidateNested()
|
||||
@Transform(({ obj, value }) => toRuleValueDto(obj.key, value))
|
||||
value!: { value: RuleValue };
|
||||
}
|
||||
|
||||
export class CreateRuleDto extends UpdateRuleDto {
|
||||
@ValidateUUID()
|
||||
albumId!: string;
|
||||
}
|
||||
|
||||
export class RuleResponseDto {
|
||||
id!: string;
|
||||
@ApiProperty({ enumName: 'RuleKey', enum: RuleKey })
|
||||
key!: RuleKey;
|
||||
@ApiProperty({ type: String })
|
||||
value!: any;
|
||||
ownerId!: string;
|
||||
}
|
||||
|
||||
export const mapRule = (rule: RuleEntity): RuleResponseDto => {
|
||||
return {
|
||||
id: rule.id,
|
||||
key: rule.key,
|
||||
value: rule.value,
|
||||
ownerId: rule.ownerId,
|
||||
};
|
||||
};
|
10
server/src/domain/rule/rule.repository.ts
Normal file
10
server/src/domain/rule/rule.repository.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { RuleEntity } from '@app/infra/entities';
|
||||
|
||||
export const IRuleRepository = 'IRuleRepository';
|
||||
|
||||
export interface IRuleRepository {
|
||||
get(id: string): Promise<RuleEntity | null>;
|
||||
create(rule: Partial<RuleEntity>): Promise<RuleEntity>;
|
||||
update(rule: Partial<RuleEntity>): Promise<RuleEntity>;
|
||||
delete(rule: RuleEntity): Promise<RuleEntity>;
|
||||
}
|
84
server/src/domain/rule/rule.service.spec.ts
Normal file
84
server/src/domain/rule/rule.service.spec.ts
Normal file
|
@ -0,0 +1,84 @@
|
|||
import { BadRequestException } from '@nestjs/common';
|
||||
import { authStub, IAccessRepositoryMock, newAccessRepositoryMock, newRuleRepositoryMock, ruleStub } from '@test';
|
||||
import { RuleKey } from '../../infra/entities/rule.entity';
|
||||
import { RuleResponseDto } from './rule.dto';
|
||||
import { IRuleRepository } from './rule.repository';
|
||||
import { RuleService } from './rule.service';
|
||||
|
||||
const responseDto: RuleResponseDto = {
|
||||
id: 'rule-1',
|
||||
key: RuleKey.CITY,
|
||||
value: 'Chandler',
|
||||
ownerId: authStub.admin.id,
|
||||
};
|
||||
|
||||
describe(RuleService.name, () => {
|
||||
let sut: RuleService;
|
||||
let accessMock: jest.Mocked<IAccessRepositoryMock>;
|
||||
let ruleMock: jest.Mocked<IRuleRepository>;
|
||||
|
||||
beforeEach(async () => {
|
||||
accessMock = newAccessRepositoryMock();
|
||||
ruleMock = newRuleRepositoryMock();
|
||||
sut = new RuleService(accessMock, ruleMock);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should require album access', async () => {
|
||||
accessMock.album.hasOwnerAccess.mockResolvedValue(false);
|
||||
accessMock.album.hasSharedAlbumAccess.mockResolvedValue(false);
|
||||
await expect(
|
||||
sut.create(authStub.admin, {
|
||||
albumId: 'not-found-album',
|
||||
key: RuleKey.CITY,
|
||||
value: { value: 'abc' },
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
expect(accessMock.album.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, 'not-found-album');
|
||||
expect(accessMock.album.hasSharedAlbumAccess).toHaveBeenCalledWith(authStub.admin.id, 'not-found-album');
|
||||
});
|
||||
|
||||
it('should create a rule', async () => {
|
||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||
ruleMock.create.mockResolvedValue(ruleStub.rule1);
|
||||
await expect(
|
||||
sut.create(authStub.admin, {
|
||||
albumId: 'album-123',
|
||||
key: RuleKey.CITY,
|
||||
value: { value: 'abc' },
|
||||
}),
|
||||
).resolves.toEqual(responseDto);
|
||||
expect(accessMock.album.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, 'album-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it('should throw a bad request when the rule is not found', async () => {
|
||||
accessMock.rule.hasOwnerAccess.mockResolvedValue(false);
|
||||
await expect(sut.get(authStub.admin, 'rule-1')).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('should get a rule by id', async () => {
|
||||
accessMock.rule.hasOwnerAccess.mockResolvedValue(true);
|
||||
ruleMock.get.mockResolvedValue(ruleStub.rule1);
|
||||
await expect(sut.get(authStub.admin, 'rule-1')).resolves.toEqual(responseDto);
|
||||
expect(ruleMock.get).toHaveBeenCalledWith('rule-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
it('should throw a bad request when the rule is not found', async () => {
|
||||
accessMock.rule.hasOwnerAccess.mockResolvedValue(false);
|
||||
await expect(
|
||||
sut.update(authStub.admin, 'rule-1', {
|
||||
key: RuleKey.CITY,
|
||||
value: { value: 'Atlanta' },
|
||||
}),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
});
|
||||
});
|
60
server/src/domain/rule/rule.service.ts
Normal file
60
server/src/domain/rule/rule.service.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||
import { RuleKey } from '../../infra/entities/rule.entity';
|
||||
import { AccessCore, IAccessRepository, Permission } from '../access';
|
||||
import { AuthUserDto } from '../auth';
|
||||
import { CreateRuleDto, mapRule, UpdateRuleDto } from './rule.dto';
|
||||
import { IRuleRepository } from './rule.repository';
|
||||
|
||||
@Injectable()
|
||||
export class RuleService {
|
||||
private access: AccessCore;
|
||||
|
||||
constructor(
|
||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
||||
@Inject(IRuleRepository) private repository: IRuleRepository,
|
||||
) {
|
||||
this.access = new AccessCore(accessRepository);
|
||||
}
|
||||
|
||||
async get(authUser: AuthUserDto, id: string) {
|
||||
await this.access.requirePermission(authUser, Permission.RULE_READ, id);
|
||||
const rule = await this.findOrFail(id);
|
||||
return mapRule(rule);
|
||||
}
|
||||
|
||||
async create(authUser: AuthUserDto, dto: CreateRuleDto) {
|
||||
await this.access.requirePermission(authUser, Permission.RULE_CREATE, dto.albumId);
|
||||
|
||||
if (dto.key === RuleKey.PERSON) {
|
||||
// TODO: validate personId
|
||||
}
|
||||
|
||||
const rule = await this.repository.create({
|
||||
key: dto.key,
|
||||
value: dto.value.value,
|
||||
albumId: dto.albumId,
|
||||
ownerId: authUser.id,
|
||||
});
|
||||
return mapRule(rule);
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, dto: UpdateRuleDto) {
|
||||
await this.access.requirePermission(authUser, Permission.RULE_UPDATE, id);
|
||||
const rule = await this.repository.update({ id, key: dto.key, value: dto.value.value });
|
||||
return mapRule(rule);
|
||||
}
|
||||
|
||||
async remove(authUser: AuthUserDto, id: string) {
|
||||
await this.access.requirePermission(authUser, Permission.RULE_DELETE, id);
|
||||
const rule = await this.findOrFail(id);
|
||||
await this.repository.delete(rule);
|
||||
}
|
||||
|
||||
private async findOrFail(id: string) {
|
||||
const rule = await this.repository.get(id);
|
||||
if (!rule) {
|
||||
throw new BadRequestException('Rule not found');
|
||||
}
|
||||
return rule;
|
||||
}
|
||||
}
|
|
@ -70,4 +70,10 @@ export class SystemConfigJobDto implements Record<QueueName, JobSettingsDto> {
|
|||
@IsObject()
|
||||
@Type(() => JobSettingsDto)
|
||||
[QueueName.SIDECAR]!: JobSettingsDto;
|
||||
|
||||
@ApiProperty({ type: JobSettingsDto })
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
@Type(() => JobSettingsDto)
|
||||
[QueueName.SMART_ALBUM]!: JobSettingsDto;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
[QueueName.STORAGE_TEMPLATE_MIGRATION]: { concurrency: 5 },
|
||||
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
|
||||
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
|
||||
[QueueName.SMART_ALBUM]: { concurrency: 1 },
|
||||
},
|
||||
oauth: {
|
||||
enabled: false,
|
||||
|
|
|
@ -29,6 +29,7 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
[QueueName.RECOGNIZE_FACES]: { concurrency: 2 },
|
||||
[QueueName.SEARCH]: { concurrency: 5 },
|
||||
[QueueName.SIDECAR]: { concurrency: 5 },
|
||||
[QueueName.SMART_ALBUM]: { concurrency: 1 },
|
||||
[QueueName.STORAGE_TEMPLATE_MIGRATION]: { concurrency: 5 },
|
||||
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
|
||||
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
OAuthController,
|
||||
PartnerController,
|
||||
PersonController,
|
||||
RuleController,
|
||||
SearchController,
|
||||
ServerInfoController,
|
||||
SharedLinkController,
|
||||
|
@ -46,6 +47,7 @@ import {
|
|||
JobController,
|
||||
OAuthController,
|
||||
PartnerController,
|
||||
RuleController,
|
||||
SearchController,
|
||||
ServerInfoController,
|
||||
SharedLinkController,
|
||||
|
|
|
@ -7,6 +7,7 @@ export * from './job.controller';
|
|||
export * from './oauth.controller';
|
||||
export * from './partner.controller';
|
||||
export * from './person.controller';
|
||||
export * from './rule.controller';
|
||||
export * from './search.controller';
|
||||
export * from './server-info.controller';
|
||||
export * from './shared-link.controller';
|
||||
|
|
44
server/src/immich/controllers/rule.controller.ts
Normal file
44
server/src/immich/controllers/rule.controller.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import {
|
||||
AuthUserDto,
|
||||
CreateRuleDto as CreateDto,
|
||||
RuleResponseDto,
|
||||
RuleService,
|
||||
UpdateRuleDto as UpdateDto,
|
||||
} from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Authenticated, AuthUser } from '../app.guard';
|
||||
import { UseValidation } from '../app.utils';
|
||||
import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('Rule')
|
||||
@Controller('rule')
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class RuleController {
|
||||
constructor(private service: RuleService) {}
|
||||
|
||||
@Post()
|
||||
createRule(@AuthUser() authUser: AuthUserDto, @Body() dto: CreateDto): Promise<RuleResponseDto> {
|
||||
return this.service.create(authUser, dto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
getRule(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<RuleResponseDto> {
|
||||
return this.service.get(authUser, id);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
updateRule(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: UpdateDto,
|
||||
): Promise<RuleResponseDto> {
|
||||
return this.service.update(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
removeRule(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto) {
|
||||
return this.service.remove(authUser, id);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import {
|
|||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { AssetEntity } from './asset.entity';
|
||||
import { RuleEntity } from './rule.entity';
|
||||
import { SharedLinkEntity } from './shared-link.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
|
||||
|
@ -52,4 +53,7 @@ export class AlbumEntity {
|
|||
|
||||
@OneToMany(() => SharedLinkEntity, (link) => link.album)
|
||||
sharedLinks!: SharedLinkEntity[];
|
||||
|
||||
@OneToMany(() => RuleEntity, (rule) => rule.album)
|
||||
rules!: RuleEntity[];
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { AssetFaceEntity } from './asset-face.entity';
|
|||
import { AssetEntity } from './asset.entity';
|
||||
import { PartnerEntity } from './partner.entity';
|
||||
import { PersonEntity } from './person.entity';
|
||||
import { RuleEntity } from './rule.entity';
|
||||
import { SharedLinkEntity } from './shared-link.entity';
|
||||
import { SmartInfoEntity } from './smart-info.entity';
|
||||
import { SystemConfigEntity } from './system-config.entity';
|
||||
|
@ -18,6 +19,7 @@ export * from './asset.entity';
|
|||
export * from './exif.entity';
|
||||
export * from './partner.entity';
|
||||
export * from './person.entity';
|
||||
export * from './rule.entity';
|
||||
export * from './shared-link.entity';
|
||||
export * from './smart-info.entity';
|
||||
export * from './system-config.entity';
|
||||
|
@ -33,6 +35,7 @@ export const databaseEntities = [
|
|||
PartnerEntity,
|
||||
PersonEntity,
|
||||
SharedLinkEntity,
|
||||
RuleEntity,
|
||||
SmartInfoEntity,
|
||||
SystemConfigEntity,
|
||||
TagEntity,
|
||||
|
|
65
server/src/infra/entities/rule.entity.ts
Normal file
65
server/src/infra/entities/rule.entity.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { JSON_TRANSFORMER } from '../infra.util';
|
||||
import { AlbumEntity } from './album.entity';
|
||||
import { UserEntity } from './user.entity';
|
||||
|
||||
@Entity('rules')
|
||||
export class RuleEntity<T = RuleValue> {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@Column()
|
||||
key!: RuleKey;
|
||||
|
||||
@Column({ type: 'varchar', transformer: JSON_TRANSFORMER })
|
||||
value!: T;
|
||||
|
||||
@Column()
|
||||
ownerId!: string;
|
||||
|
||||
@ManyToOne(() => UserEntity)
|
||||
owner!: UserEntity;
|
||||
|
||||
@Column()
|
||||
albumId!: string;
|
||||
|
||||
@ManyToOne(() => AlbumEntity, (album) => album.rules, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||
album!: AlbumEntity;
|
||||
}
|
||||
|
||||
export enum RuleKey {
|
||||
PERSON = 'person',
|
||||
TAKEN_AFTER = 'taken-after',
|
||||
CITY = 'city',
|
||||
STATE = 'state',
|
||||
COUNTRY = 'country',
|
||||
CAMERA_MAKE = 'camera-make',
|
||||
CAMERA_MODEL = 'camera-model',
|
||||
LOCATION = 'location',
|
||||
}
|
||||
|
||||
export type RuleValue = string | Date | GeoRuleValue;
|
||||
|
||||
export enum RuleValueType {
|
||||
UUID = 'uuid',
|
||||
STRING = 'string',
|
||||
DATE = 'date',
|
||||
GEO = 'geo',
|
||||
}
|
||||
|
||||
export interface GeoRuleValue {
|
||||
lat: number;
|
||||
lng: number;
|
||||
radius: number;
|
||||
}
|
||||
|
||||
export const RULE_TO_TYPE: Record<RuleKey, RuleValueType> = {
|
||||
[RuleKey.PERSON]: RuleValueType.UUID,
|
||||
[RuleKey.TAKEN_AFTER]: RuleValueType.DATE,
|
||||
[RuleKey.CITY]: RuleValueType.STRING,
|
||||
[RuleKey.STATE]: RuleValueType.STRING,
|
||||
[RuleKey.COUNTRY]: RuleValueType.STRING,
|
||||
[RuleKey.CAMERA_MAKE]: RuleValueType.STRING,
|
||||
[RuleKey.CAMERA_MODEL]: RuleValueType.STRING,
|
||||
[RuleKey.LOCATION]: RuleValueType.GEO,
|
||||
};
|
|
@ -1,13 +1,13 @@
|
|||
import { QueueName } from '@app/domain/job/job.constants';
|
||||
import { Column, Entity, PrimaryColumn } from 'typeorm';
|
||||
import { JSON_TRANSFORMER } from '../infra.util';
|
||||
|
||||
@Entity('system_config')
|
||||
export class SystemConfigEntity<T = SystemConfigValue> {
|
||||
@PrimaryColumn()
|
||||
key!: SystemConfigKey;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } })
|
||||
value!: T;
|
||||
@Column({ type: 'varchar', nullable: true, transformer: JSON_TRANSFORMER }) value!: T;
|
||||
}
|
||||
|
||||
export type SystemConfigValue = string | number | boolean;
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
immichAppConfig,
|
||||
IPartnerRepository,
|
||||
IPersonRepository,
|
||||
IRuleRepository,
|
||||
ISearchRepository,
|
||||
ISharedLinkRepository,
|
||||
ISmartInfoRepository,
|
||||
|
@ -45,6 +46,7 @@ import {
|
|||
MediaRepository,
|
||||
PartnerRepository,
|
||||
PersonRepository,
|
||||
RuleRepository,
|
||||
SharedLinkRepository,
|
||||
SmartInfoRepository,
|
||||
SystemConfigRepository,
|
||||
|
@ -67,6 +69,7 @@ const providers: Provider[] = [
|
|||
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
||||
{ provide: IPartnerRepository, useClass: PartnerRepository },
|
||||
{ provide: IPersonRepository, useClass: PersonRepository },
|
||||
{ provide: IRuleRepository, useClass: RuleRepository },
|
||||
{ provide: ISearchRepository, useClass: TypesenseRepository },
|
||||
{ provide: ISharedLinkRepository, useClass: SharedLinkRepository },
|
||||
{ provide: ISmartInfoRepository, useClass: SmartInfoRepository },
|
||||
|
|
1
server/src/infra/infra.util.ts
Normal file
1
server/src/infra/infra.util.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export const JSON_TRANSFORMER = { to: JSON.stringify, from: JSON.parse };
|
|
@ -1,13 +1,14 @@
|
|||
import { IAccessRepository } from '@app/domain';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AlbumEntity, AssetEntity, PartnerEntity, SharedLinkEntity } from '../entities';
|
||||
import { AlbumEntity, AssetEntity, PartnerEntity, RuleEntity, SharedLinkEntity } from '../entities';
|
||||
|
||||
export class AccessRepository implements IAccessRepository {
|
||||
constructor(
|
||||
@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
|
||||
@InjectRepository(AlbumEntity) private albumRepository: Repository<AlbumEntity>,
|
||||
@InjectRepository(PartnerEntity) private partnerRepository: Repository<PartnerEntity>,
|
||||
@InjectRepository(RuleEntity) private ruleRepository: Repository<RuleEntity>,
|
||||
@InjectRepository(SharedLinkEntity) private sharedLinkRepository: Repository<SharedLinkEntity>,
|
||||
) {}
|
||||
|
||||
|
@ -156,4 +157,15 @@ export class AccessRepository implements IAccessRepository {
|
|||
});
|
||||
},
|
||||
};
|
||||
|
||||
rule = {
|
||||
hasOwnerAccess: (userId: string, ruleId: string): Promise<boolean> => {
|
||||
return this.ruleRepository.exist({
|
||||
where: {
|
||||
id: ruleId,
|
||||
ownerId: userId,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
sharedUsers: true,
|
||||
assets: false,
|
||||
sharedLinks: true,
|
||||
rules: true,
|
||||
};
|
||||
|
||||
const order: FindOptionsOrder<AlbumEntity> = {};
|
||||
|
@ -44,6 +45,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
relations: {
|
||||
owner: true,
|
||||
sharedUsers: true,
|
||||
rules: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -51,7 +53,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
getByAssetId(ownerId: string, assetId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
where: { ownerId, assets: { id: assetId } },
|
||||
relations: { owner: true, sharedUsers: true },
|
||||
relations: { owner: true, sharedUsers: true, rules: true },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
|
@ -107,7 +109,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
|
||||
getOwned(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true, rules: true },
|
||||
where: { ownerId },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
@ -118,7 +120,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
*/
|
||||
getShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true, rules: true },
|
||||
where: [
|
||||
{ sharedUsers: { id: ownerId } },
|
||||
{ sharedLinks: { userId: ownerId } },
|
||||
|
@ -133,7 +135,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
*/
|
||||
getNotShared(ownerId: string): Promise<AlbumEntity[]> {
|
||||
return this.repository.find({
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true },
|
||||
relations: { sharedUsers: true, sharedLinks: true, owner: true, rules: true },
|
||||
where: { ownerId, sharedUsers: { id: IsNull() }, sharedLinks: { id: IsNull() } },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
@ -196,6 +198,7 @@ export class AlbumRepository implements IAlbumRepository {
|
|||
sharedUsers: true,
|
||||
sharedLinks: true,
|
||||
assets: true,
|
||||
rules: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ export * from './machine-learning.repository';
|
|||
export * from './media.repository';
|
||||
export * from './partner.repository';
|
||||
export * from './person.repository';
|
||||
export * from './rule.repository';
|
||||
export * from './shared-link.repository';
|
||||
export * from './smart-info.repository';
|
||||
export * from './system-config.repository';
|
||||
|
|
29
server/src/infra/repositories/rule.repository.ts
Normal file
29
server/src/infra/repositories/rule.repository.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { IRuleRepository } from '@app/domain';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { RuleEntity } from '../entities';
|
||||
|
||||
export class RuleRepository implements IRuleRepository {
|
||||
constructor(@InjectRepository(RuleEntity) private repository: Repository<RuleEntity>) {}
|
||||
|
||||
get(id: string): Promise<RuleEntity | null> {
|
||||
return this.repository.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
create(rule: Partial<RuleEntity>): Promise<RuleEntity> {
|
||||
return this.save(rule);
|
||||
}
|
||||
|
||||
update(rule: Partial<RuleEntity>): Promise<RuleEntity> {
|
||||
return this.save(rule);
|
||||
}
|
||||
|
||||
delete(rule: RuleEntity): Promise<RuleEntity> {
|
||||
return this.repository.remove(rule);
|
||||
}
|
||||
|
||||
private async save(rule: Partial<RuleEntity>): Promise<RuleEntity> {
|
||||
await this.repository.save(rule);
|
||||
return this.repository.findOneOrFail({ where: { id: rule.id } });
|
||||
}
|
||||
}
|
|
@ -74,6 +74,9 @@ export class AppService {
|
|||
[JobName.QUEUE_SIDECAR]: (data) => this.metadataService.handleQueueSidecar(data),
|
||||
[JobName.SIDECAR_DISCOVERY]: (data) => this.metadataService.handleSidecarDiscovery(data),
|
||||
[JobName.SIDECAR_SYNC]: () => this.metadataService.handleSidecarSync(),
|
||||
[JobName.SMART_ALBUM_INDEX]: (data) => {
|
||||
throw new Error('Not implemented SMART_ALBUM_INDEX');
|
||||
},
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (error: Error | any) => {
|
||||
|
|
222
server/test/e2e/rule.e2e-spec.ts
Normal file
222
server/test/e2e/rule.e2e-spec.ts
Normal file
|
@ -0,0 +1,222 @@
|
|||
import { AlbumResponseDto, LoginResponseDto } from '@app/domain';
|
||||
import { AppModule, RuleController } from '@app/immich';
|
||||
import { RuleKey } from '@app/infra/entities';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import request from 'supertest';
|
||||
import { errorStub, uuidStub } from '../fixtures';
|
||||
import { api, db } from '../test-utils';
|
||||
|
||||
describe(`${RuleController.name} (e2e)`, () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
let accessToken: string;
|
||||
let album: AlbumResponseDto;
|
||||
let loginResponse: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = await moduleFixture.createNestApplication().init();
|
||||
server = app.getHttpServer();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await db.reset();
|
||||
await api.adminSignUp(server);
|
||||
loginResponse = await api.adminLogin(server);
|
||||
accessToken = loginResponse.accessToken;
|
||||
album = await api.albumApi.create(server, accessToken, { albumName: 'New album' });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await db.disconnect();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('POST /rule', () => {
|
||||
const tests = {
|
||||
invalid: [
|
||||
{
|
||||
should: 'reject an invalid uuid',
|
||||
dto: () => ({ albumId: uuidStub.invalid, key: RuleKey.CITY, value: 'Chandler' }),
|
||||
},
|
||||
{
|
||||
should: 'reject an album that does not exist',
|
||||
dto: () => ({ albumId: uuidStub.notFound, key: RuleKey.CITY, value: 'Chandler' }),
|
||||
},
|
||||
{
|
||||
should: 'reject invalid keys',
|
||||
dto: (albumId: string) => ({ albumId, key: 'invalid', value: 'Chandler' }),
|
||||
},
|
||||
{
|
||||
should: 'validate string field values',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.CAMERA_MAKE, value: true }),
|
||||
},
|
||||
{
|
||||
should: 'validate date field values',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.TAKEN_AFTER, value: 'Chandler' }),
|
||||
},
|
||||
{
|
||||
should: 'reject an empty geo field value',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.LOCATION, value: {} }),
|
||||
},
|
||||
{
|
||||
should: 'validate geo.lat field values',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.LOCATION, value: { lat: 200, lng: 50, radius: 5 } }),
|
||||
},
|
||||
{
|
||||
should: 'validate geo.lng field values',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.LOCATION, value: { lat: 50, lng: 200, radius: 5 } }),
|
||||
},
|
||||
{
|
||||
should: 'validate geo.radius field values',
|
||||
dto: (albumId: string) => ({ albumId, key: RuleKey.LOCATION, value: { lat: 50, lng: 50, radius: false } }),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).post('/rule').send({ albumId: uuidStub.notFound });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
|
||||
for (const { should, dto } of tests.invalid) {
|
||||
it(should, async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send(dto(album.id));
|
||||
expect(status).toBe(400);
|
||||
expect(body).toEqual(errorStub.badRequest);
|
||||
});
|
||||
}
|
||||
|
||||
it('should create a rule for camera make', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.CAMERA_MAKE, value: 'Cannon' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.CAMERA_MAKE,
|
||||
value: 'Cannon',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a rule for camera model', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.CAMERA_MODEL, value: 'E0S 5D Mark III' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.CAMERA_MODEL,
|
||||
value: 'E0S 5D Mark III',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a rule for city', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.CITY, value: 'Chandler' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.CITY,
|
||||
value: 'Chandler',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a rule for state', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.STATE, value: 'Arizona' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.STATE,
|
||||
value: 'Arizona',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a rule for country', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.COUNTRY, value: 'United States' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.COUNTRY,
|
||||
value: 'United States',
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a rule with a person', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.PERSON, value: '4b5d0632-1bc1-48d1-8c89-174fd26bf29d' });
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.PERSON,
|
||||
value: '4b5d0632-1bc1-48d1-8c89-174fd26bf29d',
|
||||
});
|
||||
expect(status).toBe(201);
|
||||
});
|
||||
|
||||
it('should create a rule with taken after', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.post('/rule')
|
||||
.set('Authorization', `Bearer ${accessToken}`)
|
||||
.send({ albumId: album.id, key: RuleKey.TAKEN_AFTER, value: '2023-08-14T20:12:34.908Z' });
|
||||
expect(status).toBe(201);
|
||||
expect(body).toEqual({
|
||||
id: expect.any(String),
|
||||
ownerId: loginResponse.userId,
|
||||
key: RuleKey.TAKEN_AFTER,
|
||||
value: '2023-08-14T20:12:34.908Z',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /rule/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get(`/rule/${uuidStub.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /rule/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.put(`/rule/${uuidStub.notFound}`)
|
||||
.send({ albumId: uuidStub.notFound });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /rule/:id', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).delete(`/rule/${uuidStub.notFound}`);
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
});
|
||||
});
|
10
server/test/fixtures/album.stub.ts
vendored
10
server/test/fixtures/album.stub.ts
vendored
|
@ -17,6 +17,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
sharedWithUser: Object.freeze<AlbumEntity>({
|
||||
id: 'album-2',
|
||||
|
@ -31,6 +32,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.user1],
|
||||
rules: [],
|
||||
}),
|
||||
sharedWithMultiple: Object.freeze<AlbumEntity>({
|
||||
id: 'album-3',
|
||||
|
@ -45,6 +47,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.user1, userStub.user2],
|
||||
rules: [],
|
||||
}),
|
||||
sharedWithAdmin: Object.freeze<AlbumEntity>({
|
||||
id: 'album-3',
|
||||
|
@ -59,6 +62,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [userStub.admin],
|
||||
rules: [],
|
||||
}),
|
||||
oneAsset: Object.freeze<AlbumEntity>({
|
||||
id: 'album-4',
|
||||
|
@ -73,6 +77,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
twoAssets: Object.freeze<AlbumEntity>({
|
||||
id: 'album-4a',
|
||||
|
@ -87,6 +92,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
emptyWithInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-5',
|
||||
|
@ -101,6 +107,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
emptyWithValidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-5',
|
||||
|
@ -115,6 +122,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
oneAssetInvalidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-6',
|
||||
|
@ -129,6 +137,7 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
oneAssetValidThumbnail: Object.freeze<AlbumEntity>({
|
||||
id: 'album-6',
|
||||
|
@ -143,5 +152,6 @@ export const albumStub = {
|
|||
updatedAt: new Date(),
|
||||
sharedLinks: [],
|
||||
sharedUsers: [],
|
||||
rules: [],
|
||||
}),
|
||||
};
|
||||
|
|
2
server/test/fixtures/error.stub.ts
vendored
2
server/test/fixtures/error.stub.ts
vendored
|
@ -17,7 +17,7 @@ export const errorStub = {
|
|||
badRequest: {
|
||||
error: 'Bad Request',
|
||||
statusCode: 400,
|
||||
message: expect.any(Array),
|
||||
message: expect.anything(),
|
||||
},
|
||||
incorrectLogin: {
|
||||
error: 'Unauthorized',
|
||||
|
|
1
server/test/fixtures/index.ts
vendored
1
server/test/fixtures/index.ts
vendored
|
@ -9,6 +9,7 @@ export * from './file.stub';
|
|||
export * from './media.stub';
|
||||
export * from './partner.stub';
|
||||
export * from './person.stub';
|
||||
export * from './rule.stub';
|
||||
export * from './search.stub';
|
||||
export * from './shared-link.stub';
|
||||
export * from './system-config.stub';
|
||||
|
|
15
server/test/fixtures/rule.stub.ts
vendored
Normal file
15
server/test/fixtures/rule.stub.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { RuleEntity, RuleKey } from '@app/infra/entities';
|
||||
import { albumStub } from './album.stub';
|
||||
import { userStub } from './user.stub';
|
||||
|
||||
export const ruleStub = {
|
||||
rule1: Object.freeze<RuleEntity>({
|
||||
id: 'rule-1',
|
||||
key: RuleKey.CITY,
|
||||
value: 'Chandler',
|
||||
owner: userStub.admin,
|
||||
ownerId: userStub.admin.id,
|
||||
album: albumStub.empty,
|
||||
albumId: albumStub.empty.id,
|
||||
}),
|
||||
};
|
2
server/test/fixtures/shared-link.stub.ts
vendored
2
server/test/fixtures/shared-link.stub.ts
vendored
|
@ -80,6 +80,7 @@ const albumResponse: AlbumResponseDto = {
|
|||
hasSharedLink: false,
|
||||
assets: [],
|
||||
assetCount: 1,
|
||||
rules: [],
|
||||
};
|
||||
|
||||
export const sharedLinkStub = {
|
||||
|
@ -222,6 +223,7 @@ export const sharedLinkStub = {
|
|||
sidecarPath: null,
|
||||
},
|
||||
],
|
||||
rules: [],
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ export type IAccessRepositoryMock = {
|
|||
asset: jest.Mocked<IAccessRepository['asset']>;
|
||||
album: jest.Mocked<IAccessRepository['album']>;
|
||||
library: jest.Mocked<IAccessRepository['library']>;
|
||||
rule: jest.Mocked<IAccessRepository['rule']>;
|
||||
};
|
||||
|
||||
export const newAccessRepositoryMock = (): IAccessRepositoryMock => {
|
||||
|
@ -24,5 +25,9 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => {
|
|||
library: {
|
||||
hasPartnerAccess: jest.fn(),
|
||||
},
|
||||
|
||||
rule: {
|
||||
hasOwnerAccess: jest.fn(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ export * from './machine-learning.repository.mock';
|
|||
export * from './media.repository.mock';
|
||||
export * from './partner.repository.mock';
|
||||
export * from './person.repository.mock';
|
||||
export * from './rule.repository.mock';
|
||||
export * from './search.repository.mock';
|
||||
export * from './shared-link.repository.mock';
|
||||
export * from './smart-info.repository.mock';
|
||||
|
|
10
server/test/repositories/rule.repository.mock.ts
Normal file
10
server/test/repositories/rule.repository.mock.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { IRuleRepository } from '@app/domain';
|
||||
|
||||
export const newRuleRepositoryMock = (): jest.Mocked<IRuleRepository> => {
|
||||
return {
|
||||
get: jest.fn(),
|
||||
create: jest.fn(),
|
||||
update: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
};
|
|
@ -11,6 +11,7 @@ import {
|
|||
OAuthApi,
|
||||
PartnerApi,
|
||||
PersonApi,
|
||||
RuleApi,
|
||||
SearchApi,
|
||||
ServerInfoApi,
|
||||
SharedLinkApi,
|
||||
|
@ -30,6 +31,7 @@ export class ImmichApi {
|
|||
public keyApi: APIKeyApi;
|
||||
public oauthApi: OAuthApi;
|
||||
public partnerApi: PartnerApi;
|
||||
public ruleApi: RuleApi;
|
||||
public searchApi: SearchApi;
|
||||
public serverInfoApi: ServerInfoApi;
|
||||
public sharedLinkApi: SharedLinkApi;
|
||||
|
@ -49,6 +51,7 @@ export class ImmichApi {
|
|||
this.keyApi = new APIKeyApi(this.config);
|
||||
this.oauthApi = new OAuthApi(this.config);
|
||||
this.partnerApi = new PartnerApi(this.config);
|
||||
this.ruleApi = new RuleApi(this.config);
|
||||
this.searchApi = new SearchApi(this.config);
|
||||
this.serverInfoApi = new ServerInfoApi(this.config);
|
||||
this.sharedLinkApi = new SharedLinkApi(this.config);
|
||||
|
@ -116,6 +119,7 @@ export class ImmichApi {
|
|||
[JobName.StorageTemplateMigration]: 'Storage Template Migration',
|
||||
[JobName.BackgroundTask]: 'Background Tasks',
|
||||
[JobName.Search]: 'Search',
|
||||
[JobName.SmartAlbum]: 'Smart Albums',
|
||||
};
|
||||
|
||||
return names[jobName];
|
||||
|
|
520
web/src/api/open-api/api.ts
generated
520
web/src/api/open-api/api.ts
generated
|
@ -252,6 +252,12 @@ export interface AlbumResponseDto {
|
|||
* @memberof AlbumResponseDto
|
||||
*/
|
||||
'ownerId': string;
|
||||
/**
|
||||
*
|
||||
* @type {Array<RuleResponseDto>}
|
||||
* @memberof AlbumResponseDto
|
||||
*/
|
||||
'rules': Array<RuleResponseDto>;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
|
@ -325,6 +331,12 @@ export interface AllJobStatusResponseDto {
|
|||
* @memberof AllJobStatusResponseDto
|
||||
*/
|
||||
'sidecar': JobStatusDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobStatusDto}
|
||||
* @memberof AllJobStatusResponseDto
|
||||
*/
|
||||
'smartAlbum': JobStatusDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobStatusDto}
|
||||
|
@ -946,6 +958,33 @@ export interface CreateProfileImageResponseDto {
|
|||
*/
|
||||
'userId': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface CreateRuleDto
|
||||
*/
|
||||
export interface CreateRuleDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'albumId': string;
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof CreateRuleDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -1509,7 +1548,8 @@ export const JobName = {
|
|||
BackgroundTask: 'backgroundTask',
|
||||
StorageTemplateMigration: 'storageTemplateMigration',
|
||||
Search: 'search',
|
||||
Sidecar: 'sidecar'
|
||||
Sidecar: 'sidecar',
|
||||
SmartAlbum: 'smartAlbum'
|
||||
} as const;
|
||||
|
||||
export type JobName = typeof JobName[keyof typeof JobName];
|
||||
|
@ -1904,6 +1944,59 @@ export interface QueueStatusDto {
|
|||
*/
|
||||
'isPaused': boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @enum {string}
|
||||
*/
|
||||
|
||||
export const RuleKey = {
|
||||
Person: 'person',
|
||||
TakenAfter: 'taken-after',
|
||||
City: 'city',
|
||||
State: 'state',
|
||||
Country: 'country',
|
||||
CameraMake: 'camera-make',
|
||||
CameraModel: 'camera-model',
|
||||
Location: 'location'
|
||||
} as const;
|
||||
|
||||
export type RuleKey = typeof RuleKey[keyof typeof RuleKey];
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface RuleResponseDto
|
||||
*/
|
||||
export interface RuleResponseDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'id': string;
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'ownerId': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleResponseDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -2635,6 +2728,12 @@ export interface SystemConfigJobDto {
|
|||
* @memberof SystemConfigJobDto
|
||||
*/
|
||||
'sidecar': JobSettingsDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobSettingsDto}
|
||||
* @memberof SystemConfigJobDto
|
||||
*/
|
||||
'smartAlbum': JobSettingsDto;
|
||||
/**
|
||||
*
|
||||
* @type {JobSettingsDto}
|
||||
|
@ -3020,6 +3119,27 @@ export interface UpdateAssetDto {
|
|||
*/
|
||||
'tagIds'?: Array<string>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface UpdateRuleDto
|
||||
*/
|
||||
export interface UpdateRuleDto {
|
||||
/**
|
||||
*
|
||||
* @type {RuleKey}
|
||||
* @memberof UpdateRuleDto
|
||||
*/
|
||||
'key': RuleKey;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof UpdateRuleDto
|
||||
*/
|
||||
'value': string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -9700,6 +9820,404 @@ export class PersonApi extends BaseAPI {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* RuleApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {CreateRuleDto} createRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createRule: async (createRuleDto: CreateRuleDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'createRuleDto' is not null or undefined
|
||||
assertParamExists('createRule', 'createRuleDto', createRuleDto)
|
||||
const localVarPath = `/rule`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(createRuleDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRule: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('getRule', 'id', id)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
removeRule: async (id: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('removeRule', 'id', id)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {UpdateRuleDto} updateRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateRule: async (id: string, updateRuleDto: UpdateRuleDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('updateRule', 'id', id)
|
||||
// verify required parameter 'updateRuleDto' is not null or undefined
|
||||
assertParamExists('updateRule', 'updateRuleDto', updateRuleDto)
|
||||
const localVarPath = `/rule/{id}`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(updateRuleDto, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RuleApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = RuleApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {CreateRuleDto} createRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async createRule(createRuleDto: CreateRuleDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.createRule(createRuleDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getRule(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getRule(id, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async removeRule(id: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.removeRule(id, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {UpdateRuleDto} updateRuleDto
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async updateRule(id: string, updateRuleDto: UpdateRuleDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<RuleResponseDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateRule(id, updateRuleDto, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RuleApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const RuleApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = RuleApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiCreateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
createRule(requestParameters: RuleApiCreateRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.createRule(requestParameters.createRuleDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiGetRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getRule(requestParameters: RuleApiGetRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.getRule(requestParameters.id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiRemoveRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
removeRule(requestParameters: RuleApiRemoveRuleRequest, options?: AxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.removeRule(requestParameters.id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiUpdateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateRule(requestParameters: RuleApiUpdateRuleRequest, options?: AxiosRequestConfig): AxiosPromise<RuleResponseDto> {
|
||||
return localVarFp.updateRule(requestParameters.id, requestParameters.updateRuleDto, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Request parameters for createRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiCreateRuleRequest
|
||||
*/
|
||||
export interface RuleApiCreateRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {CreateRuleDto}
|
||||
* @memberof RuleApiCreateRule
|
||||
*/
|
||||
readonly createRuleDto: CreateRuleDto
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for getRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiGetRuleRequest
|
||||
*/
|
||||
export interface RuleApiGetRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiGetRule
|
||||
*/
|
||||
readonly id: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for removeRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiRemoveRuleRequest
|
||||
*/
|
||||
export interface RuleApiRemoveRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiRemoveRule
|
||||
*/
|
||||
readonly id: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Request parameters for updateRule operation in RuleApi.
|
||||
* @export
|
||||
* @interface RuleApiUpdateRuleRequest
|
||||
*/
|
||||
export interface RuleApiUpdateRuleRequest {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RuleApiUpdateRule
|
||||
*/
|
||||
readonly id: string
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {UpdateRuleDto}
|
||||
* @memberof RuleApiUpdateRule
|
||||
*/
|
||||
readonly updateRuleDto: UpdateRuleDto
|
||||
}
|
||||
|
||||
/**
|
||||
* RuleApi - object-oriented interface
|
||||
* @export
|
||||
* @class RuleApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class RuleApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiCreateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public createRule(requestParameters: RuleApiCreateRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).createRule(requestParameters.createRuleDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiGetRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public getRule(requestParameters: RuleApiGetRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).getRule(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiRemoveRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public removeRule(requestParameters: RuleApiRemoveRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).removeRule(requestParameters.id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {RuleApiUpdateRuleRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof RuleApi
|
||||
*/
|
||||
public updateRule(requestParameters: RuleApiUpdateRuleRequest, options?: AxiosRequestConfig) {
|
||||
return RuleApiFp(this.configuration).updateRule(requestParameters.id, requestParameters.updateRuleDto, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SearchApi - axios parameter creator
|
||||
* @export
|
||||
|
|
|
@ -57,6 +57,10 @@ input:focus-visible {
|
|||
}
|
||||
|
||||
@layer utilities {
|
||||
.immich-text-primary {
|
||||
@apply text-immich-primary dark:text-immich-dark-primary;
|
||||
}
|
||||
|
||||
.immich-form-input {
|
||||
@apply rounded-xl bg-slate-200 p-4 text-sm focus:border-immich-primary disabled:cursor-not-allowed disabled:bg-gray-400 disabled:text-gray-200 dark:bg-gray-600 dark:text-immich-dark-fg dark:disabled:bg-gray-800;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
import { PersonResponseDto, api } from '@api';
|
||||
import ControlAppBar from '$lib/components/shared-components/control-app-bar.svelte';
|
||||
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
||||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import FaceThumbnail from '$lib/components/assets/thumbnail/face-thumbnail.svelte';
|
||||
|
||||
export let selectedIds: string[] = [];
|
||||
let people: PersonResponseDto[] = [];
|
||||
let newPeople: PersonResponseDto[] = [];
|
||||
|
||||
const dispatch = createEventDispatcher<{ close: void; confirm: PersonResponseDto[] }>();
|
||||
|
||||
onMount(async () => {
|
||||
const { data } = await api.personApi.getAllPeople({ withHidden: false });
|
||||
people = data.people.filter(({ id }) => !selectedIds.includes(id));
|
||||
});
|
||||
|
||||
const handleSelection = (e: CustomEvent<{ person: PersonResponseDto }>) => {
|
||||
const person = e.detail.person;
|
||||
|
||||
if (newPeople.some((p) => p.id === person.id)) {
|
||||
newPeople = newPeople.filter((p) => p.id !== person.id);
|
||||
} else {
|
||||
newPeople = [...newPeople, person];
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<ControlAppBar showBackButton backIcon={ArrowLeft} on:close-button-click={() => dispatch('close')}>
|
||||
<svelte:fragment slot="leading">
|
||||
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">
|
||||
{#if newPeople.length === 0}
|
||||
Select faces
|
||||
{:else if newPeople.length === 1}
|
||||
1 person selected
|
||||
{:else}
|
||||
{newPeople.length} people selected
|
||||
{/if}
|
||||
</p>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="trailing">
|
||||
<Button disabled={newPeople.length === 0} size="sm" title="Confirm" on:click={() => dispatch('confirm', newPeople)}
|
||||
>Confirm</Button
|
||||
>
|
||||
</svelte:fragment>
|
||||
</ControlAppBar>
|
||||
|
||||
<div class="mt-24 flex flex-wrap gap-2 px-8">
|
||||
{#each people as person (person.id)}
|
||||
<FaceThumbnail
|
||||
{person}
|
||||
thumbnailSize={180}
|
||||
on:select={handleSelection}
|
||||
on:click={handleSelection}
|
||||
selected={newPeople.some((p) => p.id === person.id)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
|
@ -0,0 +1,116 @@
|
|||
<script lang="ts">
|
||||
import BaseModal from '$lib/components/shared-components/base-modal.svelte';
|
||||
import { PersonResponseDto, RuleKey, RuleResponseDto, api } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Plus from 'svelte-material-icons/Plus.svelte';
|
||||
import { fly } from 'svelte/transition';
|
||||
import Button from '../../elements/buttons/button.svelte';
|
||||
import Portal from '../../shared-components/portal/portal.svelte';
|
||||
import FaceSelection from './face-selection.svelte';
|
||||
|
||||
export let rules: RuleResponseDto[] = [];
|
||||
|
||||
let peopleSelection = false;
|
||||
let locationSelection = false;
|
||||
|
||||
$: peopleRules = rules.filter((rule) => rule.key === RuleKey.Person);
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
submit: RuleResponseDto[];
|
||||
close: void;
|
||||
}>();
|
||||
|
||||
const handleSelectPeople = async (people: PersonResponseDto[]) => {
|
||||
rules = [...rules, ...people.map((person) => ({ key: RuleKey.Person, value: person.id } as RuleResponseDto))];
|
||||
peopleSelection = false;
|
||||
};
|
||||
|
||||
const handleRemoveRule = async (rule: RuleResponseDto) => {
|
||||
rules = rules.filter((_rule) => rule !== _rule);
|
||||
};
|
||||
</script>
|
||||
|
||||
<BaseModal
|
||||
ignoreClickOutside
|
||||
on:close={() => {
|
||||
dispatch('close');
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="flex place-items-center gap-2">
|
||||
<p class="font-medium text-immich-fg dark:text-immich-dark-fg">Automatically add photos</p>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<section class="mb-5 px-5">
|
||||
<!-- People Selection -->
|
||||
<div id="people-selection">
|
||||
<p class="text-sm">PEOPLE</p>
|
||||
|
||||
<div class="mt-4 flex flex-wrap gap-2">
|
||||
{#each peopleRules as rule}
|
||||
<button on:click={() => handleRemoveRule(rule)}>
|
||||
<img src={api.getPeopleThumbnailUrl(rule.value)} alt={rule.value} class="h-20 w-20 rounded-lg" />
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
<button
|
||||
class="immich-text-primary border-1 flex h-20 w-20 place-content-center place-items-center rounded-lg border border-gray-300 hover:bg-gray-500/20 dark:border-gray-500"
|
||||
on:click={() => (peopleSelection = true)}
|
||||
>
|
||||
<Plus size="24" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location Selection -->
|
||||
<div id="location-selection" class="mt-5">
|
||||
<p class="text-sm">LOCATION</p>
|
||||
<div class="mt-4">
|
||||
<button
|
||||
class="immich-text-primary border-1 flex w-full place-content-center place-items-center rounded-3xl border border-gray-300 py-2 hover:bg-gray-500/20 dark:border-gray-500"
|
||||
on:click={() => (locationSelection = true)}
|
||||
>
|
||||
<Plus size="24" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date Selection -->
|
||||
<div id="date-selection" class="mt-5">
|
||||
<p class="text-sm">START DATE</p>
|
||||
<div class="mt-2">
|
||||
<div class="text-xs">
|
||||
<p>Only include photos after the set date.</p>
|
||||
<p>Include all by default</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Button size="sm">Select</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- Buttons rows -->
|
||||
<svelte:fragment slot="sticky-bottom">
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button size="sm" color="secondary" on:click={() => dispatch('close')}>Cancel</Button>
|
||||
<Button size="sm" color="primary" on:click={() => dispatch('submit', rules)}>Confirm</Button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</BaseModal>
|
||||
|
||||
<Portal target="body">
|
||||
{#if peopleSelection}
|
||||
<section
|
||||
transition:fly={{ y: 500 }}
|
||||
class="absolute left-0 top-0 z-[10000] h-full min-h-max w-full overflow-y-auto bg-gray-200 dark:bg-immich-dark-bg"
|
||||
>
|
||||
<FaceSelection
|
||||
on:close={() => (peopleSelection = false)}
|
||||
on:confirm={({ detail: people }) => handleSelectPeople(people)}
|
||||
selectedIds={peopleRules.map(({ value }) => value)}
|
||||
/>
|
||||
</section>
|
||||
{/if}
|
||||
</Portal>
|
130
web/src/lib/components/assets/thumbnail/face-thumbnail.svelte
Normal file
130
web/src/lib/components/assets/thumbnail/face-thumbnail.svelte
Normal file
|
@ -0,0 +1,130 @@
|
|||
<script lang="ts">
|
||||
import IntersectionObserver from '$lib/components/asset-viewer/intersection-observer.svelte';
|
||||
import { api, PersonResponseDto } from '@api';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import CheckCircle from 'svelte-material-icons/CheckCircle.svelte';
|
||||
|
||||
import { fade } from 'svelte/transition';
|
||||
import ImageThumbnail from './image-thumbnail.svelte';
|
||||
|
||||
export let person: PersonResponseDto;
|
||||
export let groupIndex = 0;
|
||||
export let thumbnailSize: number | undefined = undefined;
|
||||
export let thumbnailWidth: number | undefined = undefined;
|
||||
export let thumbnailHeight: number | undefined = undefined;
|
||||
export let selected = false;
|
||||
export let selectionCandidate = false;
|
||||
export let disabled = false;
|
||||
export let readonly = false;
|
||||
|
||||
let mouseOver = false;
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
click: { person: PersonResponseDto };
|
||||
select: { person: PersonResponseDto };
|
||||
'mouse-event': { isMouseOver: boolean; selectedGroupIndex: number };
|
||||
}>();
|
||||
|
||||
$: dispatch('mouse-event', { isMouseOver: mouseOver, selectedGroupIndex: groupIndex });
|
||||
|
||||
$: [width, height] = ((): [number, number] => {
|
||||
if (thumbnailSize) {
|
||||
return [thumbnailSize, thumbnailSize];
|
||||
}
|
||||
|
||||
if (thumbnailWidth && thumbnailHeight) {
|
||||
return [thumbnailWidth, thumbnailHeight];
|
||||
}
|
||||
|
||||
return [235, 235];
|
||||
})();
|
||||
|
||||
const thumbnailClickedHandler = () => {
|
||||
if (!disabled) {
|
||||
dispatch('click', { person });
|
||||
}
|
||||
};
|
||||
|
||||
const thumbnailKeyDownHandler = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
thumbnailClickedHandler();
|
||||
}
|
||||
};
|
||||
|
||||
const onIconClickedHandler = (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (!disabled) {
|
||||
dispatch('select', { person });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<IntersectionObserver once={false} let:intersecting>
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
style:width="{width}px"
|
||||
style:height="{height}px"
|
||||
class="group relative overflow-hidden {disabled
|
||||
? 'bg-gray-300'
|
||||
: 'bg-immich-primary/20 dark:bg-immich-dark-primary/20'}"
|
||||
class:cursor-not-allowed={disabled}
|
||||
class:hover:cursor-pointer={!disabled}
|
||||
on:mouseenter={() => (mouseOver = true)}
|
||||
on:mouseleave={() => (mouseOver = false)}
|
||||
on:click={thumbnailClickedHandler}
|
||||
on:keydown={thumbnailKeyDownHandler}
|
||||
>
|
||||
{#if intersecting}
|
||||
<div class="absolute z-20 h-full w-full">
|
||||
<!-- Select asset button -->
|
||||
{#if !readonly && (mouseOver || selected || selectionCandidate)}
|
||||
<button
|
||||
on:click={onIconClickedHandler}
|
||||
class="absolute p-2 focus:outline-none"
|
||||
class:cursor-not-allowed={disabled}
|
||||
role="checkbox"
|
||||
aria-checked={selected}
|
||||
{disabled}
|
||||
>
|
||||
{#if disabled}
|
||||
<CheckCircle size="24" class="text-zinc-800" />
|
||||
{:else if selected}
|
||||
<div class="rounded-full bg-[#D9DCEF] dark:bg-[#232932]">
|
||||
<CheckCircle size="24" class="text-immich-primary" />
|
||||
</div>
|
||||
{:else}
|
||||
<CheckCircle size="24" class="text-white/80 hover:text-white" />
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="absolute h-full w-full select-none bg-gray-100 transition-transform dark:bg-immich-dark-gray"
|
||||
class:scale-[0.85]={selected}
|
||||
class:rounded-xl={selected}
|
||||
>
|
||||
<!-- Gradient overlay on hover -->
|
||||
<div
|
||||
class="absolute z-10 h-full w-full bg-gradient-to-b from-black/25 via-[transparent_25%] opacity-0 transition-opacity group-hover:opacity-100"
|
||||
class:rounded-xl={selected}
|
||||
/>
|
||||
|
||||
<ImageThumbnail
|
||||
url={api.getPeopleThumbnailUrl(person.id)}
|
||||
altText={person.name}
|
||||
widthStyle="{width}px"
|
||||
heightStyle="{height}px"
|
||||
curve={selected}
|
||||
/>
|
||||
</div>
|
||||
{#if selectionCandidate}
|
||||
<div
|
||||
class="absolute top-0 h-full w-full bg-immich-primary opacity-40"
|
||||
in:fade={{ duration: 100 }}
|
||||
out:fade={{ duration: 100 }}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</IntersectionObserver>
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
const dispatch = createEventDispatcher();
|
||||
export let zIndex = 9999;
|
||||
export let ignoreClickOutside = false;
|
||||
|
||||
onMount(() => {
|
||||
if (browser) {
|
||||
|
@ -35,10 +36,10 @@
|
|||
>
|
||||
<div
|
||||
use:clickOutside
|
||||
on:outclick={() => dispatch('close')}
|
||||
class="max-h-[600px] min-h-[200px] w-[450px] rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg"
|
||||
on:outclick={() => !ignoreClickOutside && dispatch('close')}
|
||||
class="max-h-[700px] min-h-[200px] w-[450px] overflow-y-auto rounded-lg bg-immich-bg shadow-md dark:bg-immich-dark-gray dark:text-immich-dark-fg"
|
||||
>
|
||||
<div class="flex place-items-center justify-between px-5 py-3">
|
||||
<div class="sticky top-0 flex place-items-center justify-between bg-immich-bg px-5 py-3 dark:bg-immich-dark-gray">
|
||||
<div>
|
||||
<slot name="title">
|
||||
<p>Modal Title</p>
|
||||
|
@ -51,5 +52,11 @@
|
|||
<div class="">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
{#if $$slots['sticky-bottom']}
|
||||
<div class="sticky bottom-0 bg-immich-bg px-5 pb-5 pt-3 dark:bg-immich-dark-gray">
|
||||
<slot name="sticky-bottom" />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
NotificationType,
|
||||
notificationController,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import RuleSelection from '$lib/components/album-page/rule-selection-form/rule-selection-form.svelte';
|
||||
import UserAvatar from '$lib/components/shared-components/user-avatar.svelte';
|
||||
import { AppRoute, dateFormats } from '$lib/constants';
|
||||
import { createAssetInteractionStore } from '$lib/stores/asset-interaction.store';
|
||||
|
@ -33,7 +34,7 @@
|
|||
import { downloadArchive } from '$lib/utils/asset-utils';
|
||||
import { openFileUploadDialog } from '$lib/utils/file-uploader';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { TimeBucketSize, UserResponseDto, api } from '@api';
|
||||
import { RuleResponseDto, TimeBucketSize, UserResponseDto, api } from '@api';
|
||||
import ArrowLeft from 'svelte-material-icons/ArrowLeft.svelte';
|
||||
import DeleteOutline from 'svelte-material-icons/DeleteOutline.svelte';
|
||||
import DotsVertical from 'svelte-material-icons/DotsVertical.svelte';
|
||||
|
@ -41,6 +42,7 @@
|
|||
import FolderDownloadOutline from 'svelte-material-icons/FolderDownloadOutline.svelte';
|
||||
import Link from 'svelte-material-icons/Link.svelte';
|
||||
import Plus from 'svelte-material-icons/Plus.svelte';
|
||||
import FaceMan from 'svelte-material-icons/FaceMan.svelte';
|
||||
import ShareVariantOutline from 'svelte-material-icons/ShareVariantOutline.svelte';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
|
@ -57,6 +59,7 @@
|
|||
SELECT_ASSETS = 'select-assets',
|
||||
ALBUM_OPTIONS = 'album-options',
|
||||
VIEW_USERS = 'view-users',
|
||||
RULE_SELECTION = 'rule-selection',
|
||||
VIEW = 'view',
|
||||
}
|
||||
|
||||
|
@ -277,6 +280,28 @@
|
|||
handleError(error, 'Error updating album description');
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateRules = async (rules: RuleResponseDto[]) => {
|
||||
let ids = rules.filter((rule) => !!rule.id).map((rule) => rule.id);
|
||||
|
||||
for (const rule of album.rules) {
|
||||
if (!ids.includes(rule.id)) {
|
||||
await api.ruleApi.removeRule({ id: rule.id });
|
||||
}
|
||||
}
|
||||
|
||||
for (const { id, key, value } of rules) {
|
||||
if (!id) {
|
||||
await api.ruleApi.createRule({ createRuleDto: { albumId: album.id, key, value } });
|
||||
} else {
|
||||
await api.ruleApi.updateRule({ id, updateRuleDto: { key, value } });
|
||||
}
|
||||
}
|
||||
|
||||
await refreshAlbum();
|
||||
|
||||
viewMode = ViewMode.VIEW;
|
||||
};
|
||||
</script>
|
||||
|
||||
<header>
|
||||
|
@ -342,7 +367,7 @@
|
|||
<Button
|
||||
size="sm"
|
||||
rounded="lg"
|
||||
disabled={album.assetCount == 0}
|
||||
disabled={album.assetCount === 0}
|
||||
on:click={() => (viewMode = ViewMode.SELECT_USERS)}
|
||||
>
|
||||
Share
|
||||
|
@ -356,7 +381,7 @@
|
|||
<ControlAppBar on:close-button-click={handleCloseSelectAssets}>
|
||||
<svelte:fragment slot="leading">
|
||||
<p class="text-lg dark:text-immich-dark-fg">
|
||||
{#if $timelineSelected.size == 0}
|
||||
{#if $timelineSelected.size === 0}
|
||||
Add to album
|
||||
{:else}
|
||||
{$timelineSelected.size.toLocaleString($locale)} selected
|
||||
|
@ -402,7 +427,7 @@
|
|||
<!-- ALBUM TITLE -->
|
||||
<section class="pt-24">
|
||||
<input
|
||||
on:keydown={(e) => e.key == 'Enter' && titleInput.blur()}
|
||||
on:keydown={(e) => e.key === 'Enter' && titleInput.blur()}
|
||||
on:blur={handleUpdateName}
|
||||
class="w-[99%] border-b-2 border-transparent text-6xl text-immich-primary outline-none transition-all dark:text-immich-dark-primary {isOwned
|
||||
? 'hover:border-gray-400'
|
||||
|
@ -478,14 +503,26 @@
|
|||
{/if}
|
||||
|
||||
{#if album.assetCount === 0}
|
||||
<section id="empty-album" class=" mt-[200px] flex place-content-center place-items-center">
|
||||
<div class="w-[300px]">
|
||||
<p class="text-xs dark:text-immich-dark-fg">ADD PHOTOS</p>
|
||||
<section id="empty-album" class=" mt-[200px] flex flex-col place-content-center place-items-center">
|
||||
<div class="w-[340px]">
|
||||
<p class="text-sm dark:text-immich-dark-fg">ADD PHOTOS</p>
|
||||
|
||||
<button
|
||||
on:click={() => (viewMode = ViewMode.RULE_SELECTION)}
|
||||
class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||
>
|
||||
<span class="immich-text-primary"><FaceMan size="34" /> </span>
|
||||
<div class="text-left">
|
||||
<div class="text-lg">Select people & rules</div>
|
||||
<div class="text-sm">Create an auto-updating album</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
on:click={() => (viewMode = ViewMode.SELECT_ASSETS)}
|
||||
class="mt-5 flex w-full place-items-center gap-6 rounded-md border bg-immich-bg px-8 py-8 text-immich-fg transition-all hover:bg-gray-100 hover:text-immich-primary dark:border-none dark:bg-immich-dark-gray dark:text-immich-dark-fg dark:hover:text-immich-dark-primary"
|
||||
>
|
||||
<span class="text-text-immich-primary dark:text-immich-dark-primary"><Plus size="24" /> </span>
|
||||
<span class="immich-text-primary"><Plus size="34" /> </span>
|
||||
<span class="text-lg">Select photos</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -537,3 +574,11 @@
|
|||
on:updated={({ detail: description }) => handleUpdateDescription(description)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if viewMode === ViewMode.RULE_SELECTION}
|
||||
<RuleSelection
|
||||
on:close={() => (viewMode = ViewMode.VIEW)}
|
||||
rules={album.rules}
|
||||
on:submit={({ detail: rules }) => handleUpdateRules(rules)}
|
||||
/>
|
||||
{/if}
|
||||
|
|
Loading…
Reference in a new issue