Very basic but dynamically-generated config form
This commit is contained in:
@@ -1,116 +1,41 @@
|
|||||||
<pngx-page-header title="Configuration" i18n-title></pngx-page-header>
|
<pngx-page-header title="Configuration" i18n-title></pngx-page-header>
|
||||||
|
|
||||||
<form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4">
|
<form [formGroup]="configForm" (ngSubmit)="saveConfig()" class="pb-4">
|
||||||
|
|
||||||
<h4 i18n>OCR Settings</h4>
|
<ul ngbNav #nav="ngbNav" class="nav-tabs">
|
||||||
|
@for (category of optionCategories; track category) {
|
||||||
<div class="row mb-3">
|
<li [ngbNavItem]="category">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<a ngbNavLink i18n>{{category}}</a>
|
||||||
<span i18n>Output Type</span>
|
<ng-template ngbNavContent>
|
||||||
</div>
|
<div class="p-3">
|
||||||
<div class="col">
|
@for (option of getCategoryOptions(category); track option.key) {
|
||||||
<pngx-input-select [items]="ConfigChoices.output_type" formControlName="output_type" [allowNull]="true"></pngx-input-select>
|
<div class="row mb-3">
|
||||||
</div>
|
<div class="col-md-3 col-form-label pt-0">
|
||||||
</div>
|
<span i18n>{{option.title}}</span>
|
||||||
|
<p>See <a [href]="getDocsUrl(option.config_key)" target="_blank" referrerpolicy="no-referrer">{{option.config_key}}</a></p>
|
||||||
<div class="row mb-3">
|
</div>
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<div class="col">
|
||||||
<span i18n>Pages</span>
|
@switch (option.type) {
|
||||||
</div>
|
@case (ConfigOptionType.Select) { <pngx-input-select [formControlName]="option.key" [items]="option.choices" [allowNull]="true"></pngx-input-select> }
|
||||||
<div class="col">
|
@case (ConfigOptionType.Number) { <pngx-input-number [formControlName]="option.key" [showAdd]="false"></pngx-input-number> }
|
||||||
<pngx-input-number formControlName="pages" [showAdd]="false"></pngx-input-number>
|
@case (ConfigOptionType.Boolean) { <pngx-input-check [formControlName]="option.key"></pngx-input-check> }
|
||||||
</div>
|
@case (ConfigOptionType.String) { <pngx-input-text [formControlName]="option.key"></pngx-input-text> }
|
||||||
</div>
|
}
|
||||||
|
</div>
|
||||||
<div class="row mb-3">
|
</div>
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
}
|
||||||
<span i18n>Mode</span>
|
</div>
|
||||||
</div>
|
</ng-template>
|
||||||
<div class="col">
|
</li>
|
||||||
<pngx-input-select [items]="ConfigChoices.mode" formControlName="mode" [allowNull]="true"></pngx-input-select>
|
}
|
||||||
</div>
|
</ul>
|
||||||
</div>
|
<div [ngbNavOutlet]="nav" class="border-start border-end border-bottom p-3 mb-3 shadow-sm"></div>
|
||||||
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="row mb-3">
|
<div class="btn-group me-2">
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
<button type="button" (click)="discardChanges()" class="btn btn-secondary" [disabled]="loading || (isDirty$ | async) === false" i18n>Discard</button>
|
||||||
<span i18n>Skip Archive File</span>
|
</div>
|
||||||
</div>
|
<div class="btn-group">
|
||||||
<div class="col">
|
<button type="submit" class="btn btn-primary" [disabled]="loading || (isDirty$ | async) === false" i18n>Save</button>
|
||||||
<pngx-input-select [items]="ConfigChoices.skip_archive_file" formControlName="skip_archive_file" [allowNull]="true"></pngx-input-select>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Image DPI</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-number formControlName="image_dpi" [showAdd]="false"></pngx-input-number>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Clean</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-select [items]="ConfigChoices.unpaper_clean" formControlName="unpaper_clean" [allowNull]="true"></pngx-input-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Deskew</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-check formControlName="deskew"></pngx-input-check>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Rotate Pages</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-check formControlName="rotate_pages"></pngx-input-check>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Rotate Pages Threshold</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-number formControlName="rotate_pages_threshold" [showAdd]="false"></pngx-input-number>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Max Image Pixels</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-number formControlName="max_image_pixels" [showAdd]="false"></pngx-input-number>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>Color Conversion Strategy</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-select [items]="ConfigChoices.color_conversion_strategy" formControlName="color_conversion_strategy" [allowNull]="true"></pngx-input-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-3 col-form-label pt-0">
|
|
||||||
<span i18n>OCR Arguments</span>
|
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<pngx-input-text formControlName="user_args"></pngx-input-text>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary mb-2" [disabled]="loading ||(isDirty$ | async) === false" i18n>Save</button>
|
|
||||||
</form>
|
|
||||||
|
|||||||
@@ -1,22 +1,96 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
import { ComponentFixture, TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
import { ConfigComponent } from './config.component'
|
import { ConfigComponent } from './config.component'
|
||||||
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
|
import { ToastService } from 'src/app/services/toast.service'
|
||||||
|
import { of, throwError } from 'rxjs'
|
||||||
|
import { OutputTypeConfig } from 'src/app/data/paperless-config'
|
||||||
|
import { HttpClientTestingModule } from '@angular/common/http/testing'
|
||||||
|
import { BrowserModule } from '@angular/platform-browser'
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap'
|
||||||
|
import { NgSelectModule } from '@ng-select/ng-select'
|
||||||
|
import { TextComponent } from '../../common/input/text/text.component'
|
||||||
|
import { NumberComponent } from '../../common/input/number/number.component'
|
||||||
|
import { CheckComponent } from '../../common/input/check/check.component'
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
|
||||||
|
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
|
||||||
|
import { SelectComponent } from '../../common/input/select/select.component'
|
||||||
|
|
||||||
describe('ConfigComponent', () => {
|
describe('ConfigComponent', () => {
|
||||||
let component: ConfigComponent
|
let component: ConfigComponent
|
||||||
let fixture: ComponentFixture<ConfigComponent>
|
let fixture: ComponentFixture<ConfigComponent>
|
||||||
|
let configService: ConfigService
|
||||||
|
let toastService: ToastService
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [ConfigComponent],
|
declarations: [
|
||||||
|
ConfigComponent,
|
||||||
|
TextComponent,
|
||||||
|
SelectComponent,
|
||||||
|
NumberComponent,
|
||||||
|
CheckComponent,
|
||||||
|
PageHeaderComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
HttpClientTestingModule,
|
||||||
|
BrowserModule,
|
||||||
|
NgbModule,
|
||||||
|
NgSelectModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
}).compileComponents()
|
}).compileComponents()
|
||||||
|
|
||||||
|
configService = TestBed.inject(ConfigService)
|
||||||
|
toastService = TestBed.inject(ToastService)
|
||||||
fixture = TestBed.createComponent(ConfigComponent)
|
fixture = TestBed.createComponent(ConfigComponent)
|
||||||
component = fixture.componentInstance
|
component = fixture.componentInstance
|
||||||
fixture.detectChanges()
|
fixture.detectChanges()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should create', () => {
|
it('should load config on init, show error if necessary', () => {
|
||||||
expect(component).toBeTruthy()
|
const getSpy = jest.spyOn(configService, 'getConfig')
|
||||||
|
const errorSpy = jest.spyOn(toastService, 'showError')
|
||||||
|
getSpy.mockReturnValueOnce(
|
||||||
|
throwError(() => new Error('Error getting config'))
|
||||||
|
)
|
||||||
|
component.ngOnInit()
|
||||||
|
expect(getSpy).toHaveBeenCalled()
|
||||||
|
expect(errorSpy).toHaveBeenCalled()
|
||||||
|
getSpy.mockReturnValueOnce(
|
||||||
|
of({ output_type: OutputTypeConfig.PDF_A } as any)
|
||||||
|
)
|
||||||
|
component.ngOnInit()
|
||||||
|
expect(component.initialConfig).toEqual({
|
||||||
|
output_type: OutputTypeConfig.PDF_A,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should save config, show error if necessary', () => {
|
||||||
|
const saveSpy = jest.spyOn(configService, 'saveConfig')
|
||||||
|
const errorSpy = jest.spyOn(toastService, 'showError')
|
||||||
|
saveSpy.mockReturnValueOnce(
|
||||||
|
throwError(() => new Error('Error saving config'))
|
||||||
|
)
|
||||||
|
component.saveConfig()
|
||||||
|
expect(saveSpy).toHaveBeenCalled()
|
||||||
|
expect(errorSpy).toHaveBeenCalled()
|
||||||
|
saveSpy.mockReturnValueOnce(
|
||||||
|
of({ output_type: OutputTypeConfig.PDF_A } as any)
|
||||||
|
)
|
||||||
|
component.saveConfig()
|
||||||
|
expect(component.initialConfig).toEqual({
|
||||||
|
output_type: OutputTypeConfig.PDF_A,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should support discard changes', () => {
|
||||||
|
component.initialConfig = { output_type: OutputTypeConfig.PDF_A2 } as any
|
||||||
|
component.configForm.get('output_type').patchValue(OutputTypeConfig.PDF_A)
|
||||||
|
component.discardChanges()
|
||||||
|
expect(component.configForm.get('output_type').value).toEqual(
|
||||||
|
OutputTypeConfig.PDF_A2
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ import {
|
|||||||
Observable,
|
Observable,
|
||||||
Subject,
|
Subject,
|
||||||
Subscription,
|
Subscription,
|
||||||
|
first,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
} from 'rxjs'
|
} from 'rxjs'
|
||||||
import {
|
import {
|
||||||
ArchiveFileConfig,
|
PaperlessConfigOptions,
|
||||||
CleanConfig,
|
ConfigCategory,
|
||||||
ColorConvertConfig,
|
ConfigOption,
|
||||||
ModeConfig,
|
ConfigOptionType,
|
||||||
OutputTypeConfig,
|
|
||||||
PaperlessConfig,
|
PaperlessConfig,
|
||||||
} from 'src/app/data/paperless-config'
|
} from 'src/app/data/paperless-config'
|
||||||
import { ConfigService } from 'src/app/services/config.service'
|
import { ConfigService } from 'src/app/services/config.service'
|
||||||
@@ -29,32 +29,36 @@ export class ConfigComponent
|
|||||||
extends ComponentWithPermissions
|
extends ComponentWithPermissions
|
||||||
implements OnInit, OnDestroy, DirtyComponent
|
implements OnInit, OnDestroy, DirtyComponent
|
||||||
{
|
{
|
||||||
public ConfigChoices = {
|
public readonly ConfigOptionType = ConfigOptionType
|
||||||
output_type: Object.values(OutputTypeConfig),
|
|
||||||
mode: Object.values(ModeConfig),
|
|
||||||
skip_archive_file: Object.values(ArchiveFileConfig),
|
|
||||||
unpaper_clean: Object.values(CleanConfig),
|
|
||||||
color_conversion_strategy: Object.values(ColorConvertConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
public configForm = new FormGroup({
|
public configForm = new FormGroup({
|
||||||
output_type: new FormControl(null),
|
id: new FormControl(),
|
||||||
pages: new FormControl(null),
|
output_type: new FormControl(),
|
||||||
language: new FormControl(null),
|
pages: new FormControl(),
|
||||||
mode: new FormControl(null),
|
language: new FormControl(),
|
||||||
skip_archive_file: new FormControl(null),
|
mode: new FormControl(),
|
||||||
image_dpi: new FormControl(null),
|
skip_archive_file: new FormControl(),
|
||||||
unpaper_clean: new FormControl(null),
|
image_dpi: new FormControl(),
|
||||||
deskew: new FormControl(null),
|
unpaper_clean: new FormControl(),
|
||||||
rotate_pages: new FormControl(null),
|
deskew: new FormControl(),
|
||||||
rotate_pages_threshold: new FormControl(null),
|
rotate_pages: new FormControl(),
|
||||||
max_image_pixels: new FormControl(null),
|
rotate_pages_threshold: new FormControl(),
|
||||||
color_conversion_strategy: new FormControl(null),
|
max_image_pixels: new FormControl(),
|
||||||
user_args: new FormControl(null),
|
color_conversion_strategy: new FormControl(),
|
||||||
|
user_args: new FormControl(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
get optionCategories(): string[] {
|
||||||
|
return Object.values(ConfigCategory)
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategoryOptions(category: string): ConfigOption[] {
|
||||||
|
return PaperlessConfigOptions.filter((o) => o.category === category)
|
||||||
|
}
|
||||||
|
|
||||||
public loading: boolean = false
|
public loading: boolean = false
|
||||||
|
|
||||||
|
initialConfig: PaperlessConfig
|
||||||
store: BehaviorSubject<any>
|
store: BehaviorSubject<any>
|
||||||
storeSub: Subscription
|
storeSub: Subscription
|
||||||
isDirty$: Observable<boolean>
|
isDirty$: Observable<boolean>
|
||||||
@@ -69,14 +73,17 @@ export class ConfigComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this.loading = true
|
||||||
this.configService
|
this.configService
|
||||||
.getConfig()
|
.getConfig()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (config) => {
|
next: (config) => {
|
||||||
|
this.loading = false
|
||||||
this.initialize(config)
|
this.initialize(config)
|
||||||
},
|
},
|
||||||
error: (e) => {
|
error: (e) => {
|
||||||
|
this.loading = false
|
||||||
this.toastService.showError($localize`Error retrieving config`, e)
|
this.toastService.showError($localize`Error retrieving config`, e)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -88,21 +95,51 @@ export class ConfigComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initialize(config: PaperlessConfig) {
|
private initialize(config: PaperlessConfig) {
|
||||||
this.store = new BehaviorSubject(config)
|
if (!this.store) {
|
||||||
|
this.store = new BehaviorSubject(config)
|
||||||
|
|
||||||
this.store
|
this.store
|
||||||
.asObservable()
|
.asObservable()
|
||||||
.pipe(takeUntil(this.unsubscribeNotifier))
|
.pipe(takeUntil(this.unsubscribeNotifier))
|
||||||
.subscribe((state) => {
|
.subscribe((state) => {
|
||||||
this.configForm.patchValue(state, { emitEvent: false })
|
this.configForm.patchValue(state, { emitEvent: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
|
this.isDirty$ = dirtyCheck(this.configForm, this.store.asObservable())
|
||||||
|
}
|
||||||
|
|
||||||
this.configForm.patchValue(config)
|
this.configForm.patchValue(config)
|
||||||
|
|
||||||
|
this.initialConfig = config
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocsUrl(key: string) {
|
||||||
|
return `https://docs.paperless-ngx.com/configuration/#${key}`
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveConfig() {
|
public saveConfig() {
|
||||||
throw Error('Not Implemented')
|
this.loading = true
|
||||||
|
this.configService
|
||||||
|
.saveConfig(this.configForm.value as PaperlessConfig)
|
||||||
|
.pipe(takeUntil(this.unsubscribeNotifier), first())
|
||||||
|
.subscribe({
|
||||||
|
next: (config) => {
|
||||||
|
this.loading = false
|
||||||
|
this.initialize(config)
|
||||||
|
this.store.next(config)
|
||||||
|
this.toastService.showInfo($localize`Configuration updated`)
|
||||||
|
},
|
||||||
|
error: (e) => {
|
||||||
|
this.loading = false
|
||||||
|
this.toastService.showError(
|
||||||
|
$localize`An error occurred updating configuration`,
|
||||||
|
e
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public discardChanges() {
|
||||||
|
this.configForm.reset(this.initialConfig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,127 @@ export enum ColorConvertConfig {
|
|||||||
CMYK = 'CMYK',
|
CMYK = 'CMYK',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ConfigOptionType {
|
||||||
|
String = 'string',
|
||||||
|
Number = 'number',
|
||||||
|
Select = 'select',
|
||||||
|
Boolean = 'boolean',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConfigCategory = {
|
||||||
|
OCR: $localize`OCR Settings`,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ConfigOption {
|
||||||
|
key: string
|
||||||
|
title: string
|
||||||
|
type: ConfigOptionType
|
||||||
|
choices?: Array<string>
|
||||||
|
config_key?: string
|
||||||
|
category: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapToFlatChoices(enumObj: Object): Array<any> {
|
||||||
|
return Object.keys(enumObj).map((key) => {
|
||||||
|
return {
|
||||||
|
id: enumObj[key],
|
||||||
|
name: enumObj[key],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PaperlessConfigOptions: ConfigOption[] = [
|
||||||
|
{
|
||||||
|
key: 'output_type',
|
||||||
|
title: $localize`Output Type`,
|
||||||
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToFlatChoices(OutputTypeConfig),
|
||||||
|
config_key: 'PAPERLESS_OCR_OUTPUT_TYPE',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pages',
|
||||||
|
title: $localize`Pages`,
|
||||||
|
type: ConfigOptionType.Number,
|
||||||
|
config_key: 'PAPERLESS_OCR_PAGES',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mode',
|
||||||
|
title: $localize`Mode`,
|
||||||
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToFlatChoices(ModeConfig),
|
||||||
|
config_key: 'PAPERLESS_OCR_MODE',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'skip_archive_file',
|
||||||
|
title: $localize`Skip Archive File`,
|
||||||
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToFlatChoices(ArchiveFileConfig),
|
||||||
|
config_key: 'PAPERLESS_OCR_SKIP_ARCHIVE_FILE',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'image_dpi',
|
||||||
|
title: $localize`Image DPI`,
|
||||||
|
type: ConfigOptionType.Number,
|
||||||
|
config_key: 'PAPERLESS_OCR_IMAGE_DPI',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'unpaper_clean',
|
||||||
|
title: $localize`Clean`,
|
||||||
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToFlatChoices(CleanConfig),
|
||||||
|
config_key: 'PAPERLESS_OCR_CLEAN',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'deskew',
|
||||||
|
title: $localize`Deskew`,
|
||||||
|
type: ConfigOptionType.Boolean,
|
||||||
|
config_key: 'PAPERLESS_OCR_DESKEW',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rotate_pages',
|
||||||
|
title: $localize`Rotate Pages`,
|
||||||
|
type: ConfigOptionType.Boolean,
|
||||||
|
config_key: 'PAPERLESS_OCR_ROTATE_PAGES',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'rotate_pages_threshold',
|
||||||
|
title: $localize`Rotate Pages Threshold`,
|
||||||
|
type: ConfigOptionType.Number,
|
||||||
|
config_key: 'PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'max_image_pixels',
|
||||||
|
title: $localize`Max Image Pixels`,
|
||||||
|
type: ConfigOptionType.Number,
|
||||||
|
config_key: 'PAPERLESS_OCR_IMAGE_DPI',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'color_conversion_strategy',
|
||||||
|
title: $localize`Color Conversion Strategy`,
|
||||||
|
type: ConfigOptionType.Select,
|
||||||
|
choices: mapToFlatChoices(ColorConvertConfig),
|
||||||
|
config_key: 'PAPERLESS_OCR_COLOR_CONVERSION_STRATEGY',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'user_args',
|
||||||
|
title: $localize`OCR Arguments`,
|
||||||
|
type: ConfigOptionType.String,
|
||||||
|
config_key: 'PAPERLESS_OCR_USER_ARGS',
|
||||||
|
category: ConfigCategory.OCR,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export interface PaperlessConfig extends ObjectWithId {
|
export interface PaperlessConfig extends ObjectWithId {
|
||||||
output_type: OutputTypeConfig
|
output_type: OutputTypeConfig
|
||||||
pages: number
|
pages: number
|
||||||
|
|||||||
@@ -1,16 +1,42 @@
|
|||||||
import { TestBed } from '@angular/core/testing'
|
import { TestBed } from '@angular/core/testing'
|
||||||
|
|
||||||
import { ConfigService } from './config.service'
|
import { ConfigService } from './config.service'
|
||||||
|
import {
|
||||||
|
HttpClientTestingModule,
|
||||||
|
HttpTestingController,
|
||||||
|
} from '@angular/common/http/testing'
|
||||||
|
import { environment } from 'src/environments/environment'
|
||||||
|
import { OutputTypeConfig, PaperlessConfig } from '../data/paperless-config'
|
||||||
|
|
||||||
describe('ConfigService', () => {
|
describe('ConfigService', () => {
|
||||||
let service: ConfigService
|
let service: ConfigService
|
||||||
|
let httpTestingController: HttpTestingController
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({})
|
TestBed.configureTestingModule({
|
||||||
|
imports: [HttpClientTestingModule],
|
||||||
|
})
|
||||||
service = TestBed.inject(ConfigService)
|
service = TestBed.inject(ConfigService)
|
||||||
|
httpTestingController = TestBed.inject(HttpTestingController)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should call correct API endpoint on get config', () => {
|
||||||
expect(service).toBeTruthy()
|
service.getConfig().subscribe()
|
||||||
|
httpTestingController
|
||||||
|
.expectOne(`${environment.apiBaseUrl}config/`)
|
||||||
|
.flush([{}])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should call correct API endpoint on set config', () => {
|
||||||
|
service
|
||||||
|
.saveConfig({
|
||||||
|
id: 1,
|
||||||
|
output_type: OutputTypeConfig.PDF_A,
|
||||||
|
} as PaperlessConfig)
|
||||||
|
.subscribe()
|
||||||
|
const req = httpTestingController.expectOne(
|
||||||
|
`${environment.apiBaseUrl}config/1/`
|
||||||
|
)
|
||||||
|
expect(req.request.method).toEqual('PATCH')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ export class ConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
saveConfig(config: PaperlessConfig): Observable<PaperlessConfig> {
|
saveConfig(config: PaperlessConfig): Observable<PaperlessConfig> {
|
||||||
return this.http.patch<PaperlessConfig>(this.baseUrl, config).pipe(first())
|
return this.http
|
||||||
|
.patch<PaperlessConfig>(`${this.baseUrl}${config.id}/`, config)
|
||||||
|
.pipe(first())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user