Merged in build_configurations_and_cleanup (pull request #835)

Build configurations and cleanup
This commit is contained in:
Lorenz Hilpert
2019-12-11 16:10:22 +00:00
committed by Eraldo Hasanaj
121 changed files with 435 additions and 9284 deletions

View File

@@ -3,71 +3,6 @@
"version": 1,
"newProjectRoot": "apps",
"projects": {
"cat-service": {
"root": "libs/cat-service",
"sourceRoot": "libs/cat-service/src",
"projectType": "library",
"prefix": "lib",
"schematics": {
"@schematics/angular:component": {
"changeDetection": "OnPush"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "libs/cat-service/tsconfig.lib.json",
"project": "libs/cat-service/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/cat-service/src/test.ts",
"tsConfig": "libs/cat-service/tsconfig.spec.json",
"karmaConfig": "libs/cat-service/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/cat-service/tsconfig.lib.json", "libs/cat-service/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
}
}
},
"feed-service": {
"root": "libs/feed-service",
"sourceRoot": "libs/feed-service/src",
"projectType": "library",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "libs/feed-service/tsconfig.lib.json",
"project": "libs/feed-service/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/feed-service/src/test.ts",
"tsConfig": "libs/feed-service/tsconfig.spec.json",
"karmaConfig": "libs/feed-service/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/feed-service/tsconfig.lib.json", "libs/feed-service/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ui": {
"root": "libs/ui",
"sourceRoot": "libs/ui",
@@ -92,8 +27,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/ui/tsconfig.lib.json", "libs/ui/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/ui/tsconfig.lib.json",
"libs/ui/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -113,12 +53,74 @@
"main": "apps/sales/src/main.ts",
"polyfills": "apps/sales/src/polyfills.ts",
"tsConfig": "apps/sales/tsconfig.app.json",
"assets": ["apps/sales/src/favicon.ico", "apps/sales/src/assets", "apps/sales/src/manifest.webmanifest"],
"styles": ["apps/sales/src/styles.scss"],
"assets": [
"apps/sales/src/favicon.ico",
"apps/sales/src/assets",
"apps/sales/src/manifest.webmanifest"
],
"styles": [
"apps/sales/src/styles.scss"
],
"scripts": []
},
"configurations": {
"test": {
"fileReplacements": [
{
"replace": "apps/sales/src/environments/environment.ts",
"with": "apps/sales/src/environments/environment.test.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
],
"serviceWorker": true
},
"integration": {
"fileReplacements": [
{
"replace": "apps/sales/src/environments/environment.ts",
"with": "apps/sales/src/environments/environment.integration.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
],
"serviceWorker": true
},
"staging": {
"fileReplacements": [
{
"replace": "apps/sales/src/environments/environment.ts",
"with": "apps/sales/src/environments/environment.staging.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
@@ -170,9 +172,15 @@
"browserTarget": "sales:build"
},
"configurations": {
"test": {
"browserTarget": "sales:build:test"
},
"integration": {
"browserTarget": "sales:build:integration"
},
"staging": {
"browserTarget": "sales:build:staging"
},
"production": {
"browserTarget": "sales:build:production"
}
@@ -191,16 +199,27 @@
"polyfills": "apps/sales/src/polyfills.ts",
"tsConfig": "apps/sales/tsconfig.spec.json",
"karmaConfig": "apps/sales/karma.conf.js",
"styles": ["apps/sales/src/styles.scss"],
"styles": [
"apps/sales/src/styles.scss"
],
"scripts": [],
"assets": ["apps/sales/src/favicon.ico", "apps/sales/src/assets", "apps/sales/src/manifest.webmanifest"]
"assets": [
"apps/sales/src/favicon.ico",
"apps/sales/src/assets",
"apps/sales/src/manifest.webmanifest"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["apps/sales/tsconfig.app.json", "apps/sales/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"apps/sales/tsconfig.app.json",
"apps/sales/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -229,7 +248,9 @@
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": "apps/sales-e2e/tsconfig.e2e.json",
"exclude": ["**/node_modules/**"]
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -258,8 +279,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/swagger/tsconfig.lib.json", "libs/swagger/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/swagger/tsconfig.lib.json",
"libs/swagger/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -288,8 +314,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/mock/tsconfig.lib.json", "libs/mock/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/mock/tsconfig.lib.json",
"libs/mock/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -318,8 +349,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/shared/tsconfig.lib.json", "libs/shared/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/shared/tsconfig.lib.json",
"libs/shared/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
@@ -348,12 +384,17 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["libs/sso/tsconfig.lib.json", "libs/sso/tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
"tsConfig": [
"libs/sso/tsconfig.lib.json",
"libs/sso/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "sales"
}
}

View File

@@ -0,0 +1,24 @@
import { NgModule } from '@angular/core';
import {
ApiConfiguration,
AvConfiguration,
CheckoutConfiguration,
CrmConfiguration,
IsaConfiguration,
OmsConfiguration,
PrintConfiguration
} from 'swagger';
import { environment } from '../environments/environment';
@NgModule({
providers: [
{ provide: ApiConfiguration, useValue: environment.swagger.api },
{ provide: AvConfiguration, useValue: environment.swagger.av },
{ provide: CheckoutConfiguration, useValue: environment.swagger.checkout },
{ provide: CrmConfiguration, useValue: environment.swagger.crm },
{ provide: IsaConfiguration, useValue: environment.swagger.isa },
{ provide: OmsConfiguration, useValue: environment.swagger.oms },
{ provide: PrintConfiguration, useValue: environment.swagger.print }
]
})
export class AppSwaggerModule {}

View File

@@ -5,3 +5,20 @@
<app-menu [ngClass]="{ loading: loading }" *ngIf="authenticated"></app-menu>
<img *ngIf="loading" src="/assets/images/Icon_Loading.svg" class="app-loader" />
</lib-offline-overlay>
<script *ngIf="includeGoogleAnalytics">
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
(i[r] =
i[r] ||
function () {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-76423009-4', 'auto');
</script>

View File

@@ -3,6 +3,7 @@ import { SsoService } from 'sso';
import { AppService } from './core/services/app.service';
import { ModuleSwitcher } from './core/models/app-switcher.enum';
import { take } from 'rxjs/operators';
import { environment } from '../environments/environment';
@Component({
selector: 'app-root',
@@ -15,6 +16,7 @@ export class AppComponent implements OnInit {
isConnected = true;
module = ModuleSwitcher.Customer;
loading = true;
includeGoogleAnalytics = environment.includeGoogleAnalytics;
get authenticated() {
return this.ssoService.isAuthenticated();

View File

@@ -6,9 +6,6 @@ import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ComponentsModule } from './modules/components.module';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { CatServiceModule, CAT_SERVICE_ENDPOINT, CAT_AV_SERVICE_ENDPOINT } from 'cat-service';
import { ConfigService } from './core/services/config.service';
import { FeedServiceModule, FEED_SERVICE_ENDPOINT } from 'feed-service';
import { NgxsModule } from '@ngxs/store';
import { NgxsReduxDevtoolsPluginModule } from '@ngxs/devtools-plugin';
import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin';
@@ -44,6 +41,7 @@ import { BranchProcessState } from './core/store/state/branch-process.state';
import { RemissionModule } from '@isa/remission';
import { RemissionState } from './core/store/state/remission.state';
import { NgIdleKeepaliveModule } from '@ng-idle/keepalive';
import { AppSwaggerModule } from './app-swagger.module';
const states = [
AppState,
@@ -66,23 +64,6 @@ const states = [
RemissionState
];
export function _configInitializer(conf: ConfigService) {
// load config from /assets/config.json
return () => conf.load();
}
export function _catServiceEndpointProviderFactory(conf: ConfigService) {
return conf.select<string>('catService', 'endpoint', 'catService');
}
export function _catAvServiceEndpointProviderFactory(conf: ConfigService) {
return conf.select<string>('catService', 'endpoint', 'avService');
}
export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
return conf.select<string>('feedService', 'endpoint');
}
@NgModule({
declarations: [AppComponent],
imports: [
@@ -94,8 +75,6 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
NgxsModule.forRoot(states, { developmentMode: !environment.production }),
NgxsReduxDevtoolsPluginModule.forRoot(),
NgxsLoggerPluginModule,
CatServiceModule,
FeedServiceModule,
FormsModule,
ReactiveFormsModule,
SharedModule,
@@ -110,37 +89,10 @@ export function _feedServiceEndpointProviderFactory(conf: ConfigService) {
clockwise: true
}),
NgIdleKeepaliveModule.forRoot(),
RemissionModule.forRoot({
useMock: false,
endpoints: {
catsearch: 'https://isa.paragon-data.de/catsearch/v5/',
remi: 'https://isa.paragon-data.de/inv/v1/',
print: 'https://isa.paragon-data.de/print/v1/'
}
})
RemissionModule.forRoot(environment.remissionModuleOptions),
AppSwaggerModule
],
providers: [
{
provide: APP_INITIALIZER,
useFactory: _configInitializer,
multi: true,
deps: [ConfigService]
},
{
provide: CAT_SERVICE_ENDPOINT,
useFactory: _catServiceEndpointProviderFactory,
deps: [ConfigService]
},
{
provide: CAT_AV_SERVICE_ENDPOINT,
useFactory: _catAvServiceEndpointProviderFactory,
deps: [ConfigService]
},
{
provide: FEED_SERVICE_ENDPOINT,
useFactory: _feedServiceEndpointProviderFactory,
deps: [ConfigService]
},
{
provide: HTTP_INTERCEPTORS,
useClass: SsoAuthorizationInterceptor,

View File

@@ -1,11 +1,10 @@
import { CartEntry } from './cart-entry.model';
import { User } from './user.model';
import { Process } from './process.model';
import { ItemDTO, ListResponseArgsOfItemDTO } from 'swagger/swagger';
import { ItemDTO, ListResponseArgsOfItemDTO } from 'swagger';
import { RecentArticleSearch } from './recent-article-search.model';
import { ProcessBreadcrumb } from './process-breadcrumb.model';
import { ModuleSwitcher } from './app-switcher.enum';
import { ShelfCustomerOrders } from './shelf-customer-orders.model';
import { Filter } from './filter.model';
import { ProcessSelectedFilters } from './process-selected-filters.model';
import { CollectingShelfOrder } from './collecting-shelf-order.model';

View File

@@ -1,4 +1,4 @@
import { Injectable, OnDestroy, ViewChild } from '@angular/core';
import { Injectable, OnDestroy } from '@angular/core';
import { SsoService } from 'sso';
import { Store } from '@ngxs/store';
import { CountrySelector } from '../store/selectors/countries.selector';
@@ -10,7 +10,7 @@ import { Subject, of, Observable, timer } from 'rxjs';
import { takeUntil, catchError, take, filter, pairwise } from 'rxjs/operators';
import { UserStateService } from './user-state.service';
import { Meta, MetaDefinition } from '@angular/platform-browser';
import { ActivatedRoute, Router, NavigationEnd, RoutesRecognized, NavigationStart } from '@angular/router';
import { ActivatedRoute, Router, RoutesRecognized } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { isNullOrUndefined } from 'util';
import smoothscroll from 'smoothscroll-polyfill';
@@ -19,6 +19,7 @@ import { LoadVats } from '../store/actions/vat.actions';
import { LoadSuppliers } from '../store/actions/suppliers.actions';
import { RemissionSelectors } from '../store/selectors/remission.selectors';
import { Idle, DEFAULT_INTERRUPTSOURCES } from '@ng-idle/core';
import { environment } from '../../../environments/environment';
declare let ga: Function;
export const NOT_IDLE_REMISSION_REMINDER_MINUTES = 20;
@@ -216,8 +217,10 @@ export class AppService implements OnDestroy {
}
private sendNavigationEventsToGoogleAnalytics(url: string) {
ga('set', 'page', url);
ga('send', 'pageview');
if (environment.includeGoogleAnalytics && typeof ga === 'function') {
ga('set', 'page', url);
ga('send', 'pageview');
}
}
ngOnDestroy() {

View File

@@ -1,40 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap, map, distinctUntilChanged } from 'rxjs/operators';
import { isNullOrUndefined } from 'util';
import { ReplaySubject } from 'rxjs';
import { environment } from '../../../environments/environment';
import { SKIP_BASIC_AUTHORIZATION_INTERCEPTOR } from '../interceptors';
@Injectable({ providedIn: 'root' })
export class ConfigService {
private configSub = new ReplaySubject<Object>();
constructor(private httpClient: HttpClient) {}
load(): Promise<Object> {
return this.httpClient
.get(environment.config, { headers: { SKIP_BASIC_AUTHORIZATION_INTERCEPTOR } })
.pipe(tap(result => this.configSub.next(result)))
.toPromise();
}
select<T = any>(...path: string[]) {
return this.configSub.asObservable().pipe(
map(config => {
let value = config;
for (const _path of path) {
if (isNullOrUndefined(value[_path])) {
console.warn(`No configuration available for ${path.join('.')}. Add configuration to /assets/config.json.`);
return;
}
value = value[_path];
}
return value as T;
}),
distinctUntilChanged()
);
}
}

View File

@@ -1,14 +1,13 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Filter } from '../models/filter.model';
import { CatSearchService, ApiResponse, UISettingsDTO } from 'cat-service';
import { FilterItem } from '../models/filter-item.model';
import { FilterMapping } from '../mappings/filter.mapping';
import { map, filter } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import { SearchService, ResponseArgsOfUISettingsDTO } from 'swagger';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class FilterService {
constructor(private service: SearchService, private filterMapping: FilterMapping) {}
@@ -76,7 +75,7 @@ export class FilterService {
return { ...item, selected: !item.selected };
}
return { ...item };
}),
})
];
let selected = 0;
let isSelectEvent = false;
@@ -97,7 +96,7 @@ export class FilterService {
return { ...item, selected: false };
}
return { ...item };
}),
})
];
}
return updatedItems;
@@ -124,7 +123,7 @@ export class FilterService {
map((data: ResponseArgsOfUISettingsDTO) => {
const result = {
...data.result,
filter: data.result.filter.filter(f => f.key === 'format' || f.key === 'dbhwgr' || f.key === 'region'),
filter: data.result.filter.filter(f => f.key === 'format' || f.key === 'dbhwgr' || f.key === 'region')
};
return this.filterMapping.fromUiSettingsDto(result).slice(0, 3);
})

View File

@@ -2,7 +2,7 @@ import { State, Selector, Action, StateContext } from '@ngxs/store';
import { LoadAutocomplete } from '../actions/autocomplete.actions';
import { ProductService } from '../../services/product.service';
import { tap, map } from 'rxjs/operators';
import { AutocompleteDTO } from 'swagger/swagger';
import { AutocompleteDTO } from 'swagger';
import { AutocompleteResults } from '../../models/autocomplete-results.model';
const AUTOCOMPLETE_MAX_ITEMS = 10;

View File

@@ -12,13 +12,13 @@ import { Observable, Subject } from 'rxjs';
import { BarcodeScannerScanditComponent } from 'shared/lib/barcode-scanner';
import { AddBreadcrumb } from '../../../../core/store/actions/breadcrumb.actions';
import { takeUntil } from 'rxjs/operators';
import { ItemDTO } from 'cat-service';
import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors';
import { ItemDTO } from 'swagger';
@Component({
selector: 'app-barcode-search',
templateUrl: 'barcode-search.component.html',
styleUrls: ['barcode-search.component.scss'],
styleUrls: ['barcode-search.component.scss']
})
export class BarcodeSearchComponent implements OnInit, OnDestroy {
@ViewChild('scanner') scanner: BarcodeScannerScanditComponent;
@@ -42,7 +42,7 @@ export class BarcodeSearchComponent implements OnInit, OnDestroy {
fitlers: [],
take: 10,
skip: 0,
firstLoad: true,
firstLoad: true
};
this.productService
.searchItems(search)
@@ -64,7 +64,7 @@ export class BarcodeSearchComponent implements OnInit, OnDestroy {
const newProcess = <Process>{
id: 1,
name: 'Vorgang 1',
currentRoute: '/product/search',
currentRoute: '/product/search'
};
this.store.dispatch(new AddProcess(newProcess));
@@ -72,7 +72,7 @@ export class BarcodeSearchComponent implements OnInit, OnDestroy {
new AddBreadcrumb(
<Breadcrumb>{
name: 'Artikelsuche',
path: '/product/search',
path: '/product/search'
},
'product',
true

View File

@@ -9,7 +9,7 @@ import {
ChangeDetectorRef,
OnDestroy,
} from '@angular/core';
import { ModalService, ButtonComponent } from '@libs/ui';
import { ModalService } from '@libs/ui';
import { Store, Select } from '@ngxs/store';
import { ProductAvailabilityService } from '../../../../core/services/product-availability.service';
import { DatePipe } from '@angular/common';
@@ -28,8 +28,7 @@ import { isNullOrUndefined } from 'util';
import { BranchService } from '../../../../core/services/branch.service';
import { CheckoutService } from 'apps/sales/src/app/core/services/checkout.service';
import { SearchDropdownComponent } from '@libs/ui/lib/search-dropdown';
import { AddBranchesIfNotLoaded } from 'apps/sales/src/app/core/store/actions/branch.actions';
import { ItemDTO } from 'swagger/swagger';
import { ItemDTO } from 'swagger';
export interface ProcessAvailibilityMapper {
branchId: number;

View File

@@ -8,19 +8,19 @@ import { ProcessCart } from '../../../../core/models/process-cart.model';
import { CartReviewItem, deliveryFilter, cartToCartReviewArray } from '../../pages/cart-review/cart-review.component';
import { map, filter, takeUntil } from 'rxjs/operators';
import { DeliveryOption } from '../../../../core/models/delivery-option.model';
import { BranchInfoDTO } from 'dist/feed-service/feed-service';
import { User } from '../../../../core/models/user.model';
import { isArrayMinLength } from '../../../../core/utils/app.utils';
import { isNullOrUndefined } from 'util';
import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
import { cartItem } from 'apps/sales/src/app/shared/animations/cart-item.animation';
import { DeleteProductFromCart } from 'apps/sales/src/app/core/store/actions/cart.actions';
import { BranchInfoDTO } from 'swagger/lib/isa/models/branch-info-dto';
@Component({
selector: 'app-pick-up-cart',
templateUrl: './pick-up-cart.component.html',
styleUrls: ['./pick-up-cart.component.scss'],
animations: [cartItem],
animations: [cartItem]
})
export class PickUpCartComponent implements OnInit, OnDestroy {
@Select(SharedSelectors.getCart) cartData$: Observable<ProcessCart>;
@@ -83,7 +83,7 @@ export class PickUpCartComponent implements OnInit, OnDestroy {
new AddBreadcrumb(
{
name: item.product.name ? item.product.name.substring(0, 12) + (item.product.name.length > 12 ? '...' : '') : '',
path: '/product/details/' + item.id,
path: '/product/details/' + item.id
},
'product'
)

View File

@@ -4,7 +4,6 @@ import { Component, OnDestroy, OnInit, ViewChild, ChangeDetectorRef } from '@ang
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { Select, Store } from '@ngxs/store';
import { BranchInfoDTO } from '../../../../../../../../dist/feed-service/lib/dtos/branch-info.dto';
import { BookData } from '../../../../core/models/book-data.model';
import { Breadcrumb } from '../../../../core/models/breadcrumb.model';
import { Cart } from '../../../../core/models/cart.model';
@@ -25,6 +24,7 @@ import { cartPaymentType } from 'apps/sales/src/app/shared/animations/cart-payme
import { ErrorService } from 'apps/sales/src/app/core/error/component/error.service';
import { CustomerFeatures } from 'apps/sales/src/app/core/models/customer-features.model';
import { CartService } from '../services/cart.service';
import { BranchInfoDTO } from 'swagger/lib/isa/models/branch-info-dto';
export interface CartReviewItem extends BookData {
cartEntryId: number;

View File

@@ -1,14 +1,11 @@
import { Component, OnInit, Input, OnDestroy, Output, EventEmitter } from '@angular/core';
import { Product } from '../../../../core/models/product.model';
import { Router } from '@angular/router';
import { Observable, Subject } from 'rxjs';
import { ItemDTO } from 'cat-service';
import { Select, Store } from '@ngxs/store';
import { Subject } from 'rxjs';
import { Store } from '@ngxs/store';
import { AddSelectedProduct } from '../../../../core/store/actions/product.actions';
import { ChangeCurrentRoute, AllowProductLoad, AddSearch } from '../../../../core/store/actions/process.actions';
import { ChangeCurrentRoute } from '../../../../core/store/actions/process.actions';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { Search } from 'apps/sales/src/app/core/models/search.model';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { ProductService } from 'apps/sales/src/app/core/services/product.service';
import { PRODUCT_SCROLL_INDEX } from 'apps/sales/src/app/core/utils/app.constants';
import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
@@ -16,7 +13,7 @@ import { AddBreadcrumb } from 'apps/sales/src/app/core/store/actions/breadcrumb.
@Component({
selector: 'app-product-card',
templateUrl: './product-card.component.html',
styleUrls: ['./product-card.component.scss'],
styleUrls: ['./product-card.component.scss']
})
export class ProductCardComponent implements OnInit, OnDestroy {
private _product: Product;
@@ -73,7 +70,7 @@ export class ProductCardComponent implements OnInit, OnDestroy {
new AddBreadcrumb(
{
name: product.title ? product.title.substring(0, 12) + (product.title.length > 12 ? '...' : '') : '',
path: '/product/details/' + product.id,
path: '/product/details/' + product.id
},
'product'
)

View File

@@ -3,14 +3,13 @@ import { Component, OnInit, ViewChild, OnDestroy, ChangeDetectionStrategy, Chang
import { Search } from '../../../../core/models/search.model';
import { Process } from '../../../../core/models/process.model';
import { Product } from '../../../../core/models/product.model';
import { ActivatedRoute, Router, NavigationStart } from '@angular/router';
import { Router } from '@angular/router';
import { Select, Store } from '@ngxs/store';
import { ItemDTO } from 'dist/cat-service/lib/dtos';
import { Observable, Subject, of } from 'rxjs';
import { staggerAnimation } from './stagger.animation';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { SearchDataSource } from './product-data.datasource';
import { takeUntil, filter, take, distinctUntilChanged, switchMap, map, debounceTime, debounce, takeLast, last } from 'rxjs/operators';
import { takeUntil, filter, take, distinctUntilChanged, switchMap, map } from 'rxjs/operators';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors';
import { isNullOrUndefined } from 'util';
@@ -24,8 +23,8 @@ import { ProductUtilService } from 'apps/sales/src/app/core/services/product-uti
import { Side } from '@libs/ui/lib/small-double-choice-switch';
import { LoadFilters, SetFilterType, ClearNegativeFilters } from 'apps/sales/src/app/core/store/actions/filter.actions';
import { FilterType } from 'apps/sales/src/app/core/models/filter-type.enum';
import { FilterComponent } from '../product-search/components/filter/filter.component';
import { ListRange } from '@angular/cdk/collections';
import { ItemDTO } from 'swagger';
@Component({
selector: 'app-product-results',

View File

@@ -33,7 +33,7 @@ import { Filter } from '../../../../../../core/models/filter.model';
import { FilterItem } from '../../../../../../core/models/filter-item.model';
import { SharedSelectors } from 'apps/sales/src/app/core/store/selectors/shared.selectors';
import { AppService } from 'apps/sales/src/app/core/services/app.service';
import { ListResponseArgsOfItemDTO } from 'swagger/swagger';
import { ListResponseArgsOfItemDTO } from 'swagger';
import { FilterType } from 'apps/sales/src/app/core/models/filter-type.enum';
import { SetFilterType } from 'apps/sales/src/app/core/store/actions/filter.actions';

View File

@@ -0,0 +1,35 @@
export const environment = {
production: false,
includeGoogleAnalytics: false,
swagger: {
api: {
rootUrl: 'https://isa-integration.paragon-data.net/catsearch/v5'
},
av: {
rootUrl: 'https://isa-integration.paragon-data.net/ava/v4'
},
checkout: {
rootUrl: 'https://isa-integration.paragon-data.net/checkout/v3'
},
crm: {
rootUrl: 'https://isa-integration.paragon-data.net/crm/v2'
},
isa: {
rootUrl: 'https://isa-integration.paragon-data.net/isa/v1'
},
oms: {
rootUrl: 'https://isa-integration.paragon-data.net/oms/v2'
},
print: {
rootUrl: 'https://isa-integration.paragon-data.net/print/v1'
}
},
remissionModuleOptions: {
useMock: false,
endpoints: {
catsearch: 'https://isa-integration.paragon-data.net/catsearch/v5/',
remi: 'https://isa-integration.paragon-data.net/inv/v1/',
print: 'https://isa-integration.paragon-data.net/print/v1/'
}
}
};

View File

@@ -1,4 +1,35 @@
export const environment = {
production: true,
config: '/assets/config.json'
includeGoogleAnalytics: true,
swagger: {
api: {
rootUrl: 'https://isa.paragon-data.net/catsearch/v5'
},
av: {
rootUrl: 'https://isa.paragon-data.net/ava/v4'
},
checkout: {
rootUrl: 'https://isa.paragon-data.net/checkout/v3'
},
crm: {
rootUrl: 'https://isa.paragon-data.net/crm/v2'
},
isa: {
rootUrl: 'https://isa.paragon-data.net/isa/v1'
},
oms: {
rootUrl: 'https://isa.paragon-data.net/oms/v2'
},
print: {
rootUrl: 'https://isa.paragon-data.net/print/v1'
}
},
remissionModuleOptions: {
useMock: false,
endpoints: {
catsearch: 'https://isa.paragon-data.net/catsearch/v5/',
remi: 'https://isa.paragon-data.net/inv/v1/',
print: 'https://isa.paragon-data.net/print/v1/'
}
}
};

View File

@@ -0,0 +1,35 @@
export const environment = {
production: true,
includeGoogleAnalytics: true,
swagger: {
api: {
rootUrl: 'https://isa-staging.paragon-data.net/catsearch/v5'
},
av: {
rootUrl: 'https://isa-staging.paragon-data.net/ava/v4'
},
checkout: {
rootUrl: 'https://isa-staging.paragon-data.net/checkout/v3'
},
crm: {
rootUrl: 'https://isa-staging.paragon-data.net/crm/v2'
},
isa: {
rootUrl: 'https://isa-staging.paragon-data.net/isa/v1'
},
oms: {
rootUrl: 'https://isa-staging.paragon-data.net/oms/v2'
},
print: {
rootUrl: 'https://isa-staging.paragon-data.net/print/v1'
}
},
remissionModuleOptions: {
useMock: false,
endpoints: {
catsearch: 'https://isa-staging.paragon-data.net/catsearch/v5/',
remi: 'https://isa-staging.paragon-data.net/inv/v1/',
print: 'https://isa-staging.paragon-data.net/print/v1/'
}
}
};

View File

@@ -0,0 +1,35 @@
export const environment = {
production: false,
includeGoogleAnalytics: false,
swagger: {
api: {
rootUrl: 'https://isa-test.paragon-data.net/catsearch/v5'
},
av: {
rootUrl: 'https://isa-test.paragon-data.net/ava/v4'
},
checkout: {
rootUrl: 'https://isa-test.paragon-data.net/checkout/v3'
},
crm: {
rootUrl: 'https://isa-test.paragon-data.net/crm/v2'
},
isa: {
rootUrl: 'https://isa-test.paragon-data.net/isa/v1'
},
oms: {
rootUrl: 'https://isa-test.paragon-data.net/oms/v2'
},
print: {
rootUrl: 'https://isa-test.paragon-data.net/print/v1'
}
},
remissionModuleOptions: {
useMock: false,
endpoints: {
catsearch: 'https://isa-test.paragon-data.net/catsearch/v5/',
remi: 'https://isa-test.paragon-data.net/inv/v1/',
print: 'https://isa-test.paragon-data.net/print/v1/'
}
}
};

View File

@@ -1,17 +1,35 @@
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
config: '/assets/config.json'
includeGoogleAnalytics: false,
swagger: {
api: {
rootUrl: 'https://isa-test.paragon-data.net/catsearch/v5'
},
av: {
rootUrl: 'https://isa-test.paragon-data.net/ava/v4'
},
checkout: {
rootUrl: 'https://isa-test.paragon-data.net/checkout/v3'
},
crm: {
rootUrl: 'https://isa-test.paragon-data.net/crm/v2'
},
isa: {
rootUrl: 'https://isa-test.paragon-data.net/isa/v1'
},
oms: {
rootUrl: 'https://isa-test.paragon-data.net/oms/v2'
},
print: {
rootUrl: 'https://isa-test.paragon-data.net/print/v1'
}
},
remissionModuleOptions: {
useMock: false,
endpoints: {
catsearch: 'https://isa-test.paragon-data.net/catsearch/v5/',
remi: 'https://isa-test.paragon-data.net/inv/v1/',
print: 'https://isa-test.paragon-data.net/print/v1/'
}
}
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@@ -1,65 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hima</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" />
<link rel="manifest" href="manifest.webmanifest" crossorigin="use-credentials" />
<meta name="theme-color" content="#1976d2" />
<style>
.app-loader {
position: absolute;
top: 50%;
left: 50%;
margin: -20px 0 0 -20px;
-webkit-animation: spin 1.5s linear infinite;
-moz-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-moz-keyframes spin {
100% {
-moz-transform: rotate(360deg);
}
}
@-webkit-keyframes spin {
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
<script>
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
(i[r] =
i[r] ||
function() {
(i[r].q = i[r].q || []).push(arguments);
}),
(i[r].l = 1 * new Date());
(a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
<head>
<meta charset="utf-8" />
<title>Hima</title>
<base href="/" />
ga('create', 'UA-76423009-4', 'auto');
</script>
</head>
<body>
<app-root>
<img src="/assets/images/Icon_Loading.svg" class="app-loader" />
</app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700" rel="stylesheet" />
<link rel="manifest" href="manifest.webmanifest" crossorigin="use-credentials" />
<meta name="theme-color" content="#1976d2" />
<style>
.app-loader {
position: absolute;
top: 50%;
left: 50%;
margin: -20px 0 0 -20px;
-webkit-animation: spin 1.5s linear infinite;
-moz-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
@-moz-keyframes spin {
100% {
-moz-transform: rotate(360deg);
}
}
@-webkit-keyframes spin {
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<app-root>
<img src="/assets/images/Icon_Loading.svg" class="app-loader" />
</app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>

View File

@@ -1,31 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@@ -1,7 +0,0 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/cat-service",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@@ -1,8 +0,0 @@
{
"name": "cat-service",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^7.2.0",
"@angular/core": "^7.2.0"
}
}

View File

@@ -1,81 +0,0 @@
# CatService
## ApiResponse and PagedApiResponse
All issued requests with the status code 200 will return an ApiResponse or PagedApiResponse.
```
export class ApiResponse<T = void> {
requestId: number;
message: string; // response message
error: boolean; // true / false if the request faild ind is properly handled by the backend...
invalidProperties: StringDictionary<string>; // dictionary of invalid request properties
result: T; // result
}
```
The PagedApiResponse will also include paging informations:
```
export class PagedApiResponse<T> extends ApiResponse<T[]> {
skip: number;
take: number;
hits: number;
}
```
## CatSearchService
### Functions
**settings()**: ApiResponse<UISettingsDTO>
Default and possible settings for the **search(query: QueryTokenDTO)** call.
It also includes a imageUrl property with an placeholder for loading product images.
*https://produktbilder.ihugendubel.de/{ean}_150x150.jpg?showDummy=true* => replace {ean} with the ean from the ItemDTO to get the product image.
**search(query: QueryTokenDTO)**: PagedApiResponse<ItemDTO>
The search request will only return a fraction of the ItemDTO. To geht the full information use *getById(id: number)*
```
const searchService = new CatSearchService(...);
const filter: QueryTokenDTO = {
// you can get possible input informations from *UISettingsDTO.input*
input: { 'qs': 'my search term' },
// you can get possible sort informations from *UISettingsDTO.orderBy*
sort: [{ by: 'rank', asc: true }],
// you can get possible filter informations from *UISettingsDTO.filter*
filter: {
'instock': 'false'
}
}
searchService(filter);
```
**complete(query: AutocompleteTokenDTO)**: Observable<PagedApiResponse<ItemDTO>>
Get suggestions for autocompletion
```
const searchService = new CatSearchService(...);
searchService.complete({ input: 'harry' });
```
**getById(id: number)**: ApiResponse<ItemDTO>
Get the full information for an ItemDTO.

View File

@@ -1,56 +0,0 @@
import { TestBed, getTestBed } from '@angular/core/testing';
import { CatAvailabilityService } from './cat-availability.service';
import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing';
import { CAT_AV_SERVICE_ENDPOINT } from './tokens';
import { AvailabilityRequestDTO } from './dtos';
describe('CatAvailabilityService', () => {
const endpoint = 'https://fake-endpoint.de';
let injector: TestBed;
let service: CatAvailabilityService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
CatAvailabilityService,
{ provide: CAT_AV_SERVICE_ENDPOINT, useValue: endpoint }
]
});
injector = getTestBed();
service = injector.get(CatAvailabilityService);
httpMock = injector.get(HttpTestingController);
});
describe('#get', () => {
it('should issue a POST request to /ola/availability', () => {
service.get([]).subscribe();
const req = httpMock.expectOne({ method: 'POST', url: `${endpoint}/ola/availability` });
});
it('should have the argument in the request body', () => {
const av: AvailabilityRequestDTO[] = [{ ean: '123456789', qty: 1, supplier: 'L', shopId: 1 }];
service.get(av).subscribe();
const req = httpMock.expectOne({ method: 'POST', url: `${endpoint}/ola/availability` });
expect(req.request.body).toBe(av);
});
it('should throw an Error if the validation fails', () => {
const av: AvailabilityRequestDTO[] = [null];
service.get(av).subscribe(
() => { console.log('fsag'); },
(error) => {
expect(error).toBeDefined();
}
);
httpMock.expectNone({ method: 'POST', url: `${endpoint}/ola/availability` });
});
});
});

View File

@@ -1,48 +0,0 @@
import { Injectable, Inject } from '@angular/core';
import { AvailabilityRequestDTO, ArticleAvailabilityDTO } from './dtos';
import { HttpClient } from '@angular/common/http';
import { CAT_AV_SERVICE_ENDPOINT } from './tokens';
import { Observable, isObservable, throwError, } from 'rxjs';
import { ApiResponse } from './response';
import { availabilityRequestValidator } from './validators';
@Injectable({ providedIn: 'root' })
export class CatAvailabilityService {
endpoint = '';
constructor(
private http: HttpClient,
@Inject(CAT_AV_SERVICE_ENDPOINT) endpoint: string | Observable<string>
) {
if (isObservable(endpoint)) {
endpoint.subscribe(e => this.endpoint = e);
} else {
this.endpoint = endpoint;
}
}
/**
* Get estimated delivery date
* Required fields
* - itemId or ean
* - shopId => Shop
* - qty => Quantity
* - supplier => Supplier
* @param req AvailabilityRequestDTO[]
* @returns returns an Observable of ApiResponse<ArticleAvailabilityDTO[]>
*/
get(req: AvailabilityRequestDTO[]): Observable<ApiResponse<ArticleAvailabilityDTO[]>> {
for (const result of req.map(item => availabilityRequestValidator(item))) {
if (!result.valid) {
return throwError(new Error(result.message));
}
}
return this.http.post<ApiResponse<ArticleAvailabilityDTO[]>>(
`${this.endpoint}/ola/availability`,
req
);
}
}

View File

@@ -1,50 +0,0 @@
import { CatImageService } from './cat-image.service';
import { TestBed } from '@angular/core/testing';
import { CatSearchMockService } from './cat-search-mock.service';
import { CatSearchService } from './cat-search.service';
import { of } from 'rxjs';
describe('CatImageService', () => {
let service: CatImageService;
let mockService: CatSearchMockService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [{ provide: CatSearchService, useClass: CatSearchMockService }]
});
service = TestBed.get(CatImageService);
mockService = TestBed.get(CatSearchService);
});
it('should create the service', () => {
expect(service).toBeDefined();
});
describe('#getImageUrl', () => {
it('should return an url', () => {
spyOn(mockService, 'settings').and.returnValue(of({
result: { imageUrl: 'https://produktbilder.ihugendubel.de/{ean}_150x150.jpg?showDummy=true' }
}));
service.getImageUrl('testean123').subscribe((url) => {
expect(url).toBe('https://produktbilder.ihugendubel.de/testean123_150x150.jpg?showDummy=true');
});
});
it('should replace the image size', () => {
spyOn(mockService, 'settings').and.returnValue(of({
result: { imageUrl: 'https://produktbilder.ihugendubel.de/{ean}_150x150.jpg?showDummy=true' }
}));
service.getImageUrl('testean123', { width: 350, height: 350 }).subscribe((url) => {
expect(url).toBe('https://produktbilder.ihugendubel.de/testean123_350x350.jpg?showDummy=true');
});
});
});
});

View File

@@ -1,21 +0,0 @@
import { Injectable } from '@angular/core';
import { ItemDTO } from './dtos';
import { CatSearchService } from './cat-search.service';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class CatImageService {
constructor(private catSearch: CatSearchService) { }
getImageUrl(ean: string, size: { width: number; height: number } = { width: 150, height: 150 }): Observable<string> {
return this.catSearch.settings().pipe(
map(response => response.result.imageUrl),
map(url => url.replace('{ean}', ean)),
map(url => url.replace('150x150', `${size.width}x${size.height}`)),
catchError(() => of(''))
);
}
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,82 +0,0 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { ItemDTO } from './dtos/data';
import { PagedApiResponse, ApiResponse } from './response';
import { QueryTokenDTO, AutocompleteTokenDTO } from './dtos/query';
import { CAT_SEARCH_MOCK_DATA, CAT_SEARCH_SETTINGS } from './cat-search-mock.data';
import { map } from 'rxjs/operators';
import { UISettingsDTO } from './dtos/ui';
@Injectable()
export class CatSearchMockService {
constructor(
) {
}
search(query: QueryTokenDTO): Observable<PagedApiResponse<ItemDTO>> {
return of([...CAT_SEARCH_MOCK_DATA]).pipe(
map(items => items.slice(query.skip || 0, query.take || 50)),
map(items => ({
error: false,
hits: CAT_SEARCH_MOCK_DATA.length,
invalidProperties: {},
message: '',
requestId: 0,
result: items,
skip: query.skip || 0,
take: query.take || 50
}) as PagedApiResponse<ItemDTO>)
);
}
complete(token: AutocompleteTokenDTO): Observable<PagedApiResponse<string>> {
return of({
error: false,
hits: 4,
invalidProperties: {},
message: '',
requestId: 0,
result: [
token.input,
`${token.input} aa`,
`${token.input} bb`,
`${token.input} cc`
],
skip: 0,
take: 50
});
}
getById(id: number): Observable<ApiResponse<ItemDTO>> {
return of([...CAT_SEARCH_MOCK_DATA]).pipe(
map(items => items.find(f => f.id === id)),
map((item) => {
const result: ApiResponse<ItemDTO> = {
error: !item,
message: `Kein Artikel mit der Id ${id} verfügbar.`,
invalidProperties: {},
requestId: 0,
result: item
};
return result;
})
);
}
settings(): Observable<ApiResponse<UISettingsDTO>> {
return of(CAT_SEARCH_SETTINGS as ApiResponse<UISettingsDTO>);
}
}

View File

@@ -1,159 +0,0 @@
import { TestBed, getTestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { CatSearchService } from './cat-search.service';
import { QueryTokenDTO, AutocompleteTokenDTO } from './dtos/query';
import { CAT_SERVICE_ENDPOINT } from './tokens';
describe('CatSearchService', () => {
let injector: TestBed;
let service: CatSearchService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
CatSearchService,
{ provide: CAT_SERVICE_ENDPOINT, useValue: '' }
]
});
injector = getTestBed();
service = injector.get(CatSearchService);
httpMock = injector.get(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('#top', () => {
it('should issue a POST /s/top request', () => {
service.top({ input: { qs: 'test' } }).subscribe();
httpMock.expectOne({ method: 'POST', url: '/s/top' });
});
it('should have a Request.body', () => {
service.top({
input: { qs: 'test' },
skip: 50,
take: 25
}).subscribe();
const req = httpMock.expectOne({ method: 'POST', url: '/s/top' });
const body: QueryTokenDTO = req.request.body;
expect(body.input['qs']).toBe('test');
expect(body.take).toBe(25);
expect(body.skip).toBe(50);
});
});
describe('#search', () => {
it('should issue a POST /s request', () => {
service.search({ input: { qs: 'test' } }).subscribe();
httpMock.expectOne({ method: 'POST', url: '/s' });
});
it('should have a Request.body', () => {
service.search({
input: { qs: 'test' },
skip: 50,
take: 25
}).subscribe();
const req = httpMock.expectOne({ method: 'POST', url: '/s' });
const body: QueryTokenDTO = req.request.body;
expect(body.input['qs']).toBe('test');
expect(body.take).toBe(25);
expect(body.skip).toBe(50);
});
});
describe('#complete', () => {
it('should issue a POST /s/complete request', () => {
service.complete({ input: 'test' }).subscribe();
httpMock.expectOne({ method: 'POST', url: '/s/complete' });
});
it('should have a Request.body', () => {
service.complete({
input: 'test',
take: 25
}).subscribe();
const req = httpMock.expectOne({ method: 'POST', url: '/s/complete' });
const body: AutocompleteTokenDTO = req.request.body;
expect(body.input).toBe('test');
expect(body.take).toBe(25);
});
});
describe('#getByIds', () => {
it('should issue a POST /s/byid request', () => {
service.getByIds([1, 2, 3]).subscribe();
httpMock.expectOne({ method: 'post', url: '/s/byid' });
});
it('should set the request body', () => {
service.getByIds([1, 2, 3]).subscribe();
const req = httpMock.expectOne({ method: 'post', url: '/s/byid' });
expect(req.request.body.join(', ')).toBe([1, 2, 3].join(', '));
});
});
describe('#getById', () => {
it('should issue a POST /s/{id} request', () => {
service.getById(1).subscribe();
httpMock.expectOne({ method: 'GET', url: '/s/1' });
});
});
describe('#settings', () => {
it('should issue a GET /s/settings request', () => {
service.settings().subscribe();
httpMock.expectOne({ method: 'GET', url: '/s/settings' });
});
it('should return the cached response', () => {
service.settings().subscribe();
httpMock.expectOne({ method: 'GET', url: '/s/settings' });
service.settings().subscribe();
httpMock.expectNone({ method: 'GET', url: '/s/settings' });
});
it('should force a second response', () => {
service.settings().subscribe();
httpMock.expectOne({ method: 'GET', url: '/s/settings' });
service.settings({ force: true }).subscribe();
httpMock.expectOne({ method: 'GET', url: '/s/settings' });
});
});
});

View File

@@ -1,108 +0,0 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, isObservable } from 'rxjs';
import { ItemDTO } from './dtos/data';
import { PagedApiResponse, ApiResponse } from './response';
import { CAT_SERVICE_ENDPOINT } from './tokens';
import { QueryTokenDTO, AutocompleteTokenDTO } from './dtos/query';
import { UISettingsDTO } from './dtos/ui';
import { shareReplay } from 'rxjs/operators';
import { DEFAULT_QUERY_TOKEN_DTO } from './defaults/query-token-dto';
@Injectable({ providedIn: 'root' })
export class CatSearchService {
settingsCache$: Observable<ApiResponse<UISettingsDTO>>;
endpoint = '';
constructor(
private http: HttpClient,
@Inject(CAT_SERVICE_ENDPOINT) endpoint: string | Observable<string>
) {
if (isObservable(endpoint)) {
endpoint.subscribe(e => this.endpoint = e);
} else {
this.endpoint = endpoint;
}
}
/**
* TOP-Search
* @param query QueryTokenDTO
* @returns returns an Observable of PagedApiResult<ItemDTO>
*/
top(query: QueryTokenDTO): Observable<PagedApiResponse<ItemDTO>> {
return this.http
.post<PagedApiResponse<ItemDTO>>(
`${this.endpoint}/s/top`,
{ ...DEFAULT_QUERY_TOKEN_DTO, ...query }
);
}
/**
* Search items
* @param query QueryTokenDTO
* @returns returns an Observable of PagedApiResult<ItemDTO>
*/
search(query: QueryTokenDTO): Observable<PagedApiResponse<ItemDTO>> {
return this.http
.post<PagedApiResponse<ItemDTO>>(
`${this.endpoint}/s`,
{ ...DEFAULT_QUERY_TOKEN_DTO, ...query }
);
}
/**
* Autocomplete for search
* @param query AutocompleteTokenDTO
* @return returns an Observable of PagedApiResult<string>
*/
complete(token: AutocompleteTokenDTO): Observable<PagedApiResponse<string>> {
return this.http.post<PagedApiResponse<string>>(
`${this.endpoint}/s/complete`,
token
);
}
/**
* Get item by ids
* @param id ItemDTO.id
* @returns returns an Observable of PagedApiResult<ItemDTO>
*/
getByIds(ids: number[]): Observable<PagedApiResponse<ItemDTO>> {
return this.http
.post<PagedApiResponse<ItemDTO>>(`${this.endpoint}/s/byid`, ids);
}
/**
* Get item by id
* @param id ItemDTO.id
* @returns returns an Observable of ApiResult<ItemDTO>
*/
getById(id: number): Observable<ApiResponse<ItemDTO>> {
return this.http
.get<ApiResponse<ItemDTO>>(`${this.endpoint}/s/${id}`);
}
/**
* Settings
* @param option force?: boolean
* @returns returns an Observable of ApiResult<UISettingsDTO>
*/
settings(option?: { force?: boolean }): Observable<ApiResponse<UISettingsDTO>> {
if ((!this.settingsCache$) || (option && option.force)) {
this.settingsCache$ = this.http.get<ApiResponse<UISettingsDTO>>(
`${this.endpoint}/s/settings`
).pipe(
shareReplay()
);
}
return this.settingsCache$;
}
}

View File

@@ -1,14 +0,0 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CAT_SERVICE_ENDPOINT } from './tokens';
@NgModule()
export class CatServiceModule {
static forRoot(endpoint: string): ModuleWithProviders {
return {
ngModule: CatServiceModule,
providers: [ { provide: CAT_SERVICE_ENDPOINT, useValue: endpoint } ]
};
}
}

View File

@@ -1,5 +0,0 @@
import { QueryTokenDTO } from '../dtos';
export const DEFAULT_QUERY_TOKEN_DTO: Partial<QueryTokenDTO> = {
take: 10
};

View File

@@ -1,5 +0,0 @@
import { UISettingsDTO } from './ui';
export interface CartSearchSettings {
uiSettings: UISettingsDTO;
}

View File

@@ -1,84 +0,0 @@
import { PriceDTO } from './price.dto';
import { AvailabilityType } from '../../enums';
export interface ArticleAvailabilityDTO {
/**
* Produkt / Artikel PK
*/
itemId?: number;
/**
* Eindeutige Referenz zur Zuordnung
*/
requestReference: string;
/**
* EAN
*/
ean: string;
/**
* Shop Id
*/
shop?: number;
/**
* Price
*/
price: PriceDTO;
/**
* Lieferant
*/
supplier: string;
/**
* Stock Status Code / Meldeschlüssel
*/
ssc: string;
/**
* Verfügbare Menge
*/
qty?: number;
/**
* Vorgemerkt
*/
isPrebooked?: boolean;
/**
* Voraussichtliches Lieferdatum
*/
at?: Date;
/**
* Alternatives Voraussichtliches Lieferdatum
*/
altAt?: Date;
/**
* Verfügbarkeitsstatus
*/
availabilityType: AvailabilityType;
/**
* Rang
*/
preferred?: number;
/**
* Zeitstemple der Anfrage
*/
requested?: Date;
/**
* StatusCode der Verfügbarkeitsanfrage
*/
requestStatusCode: string;
/**
* Beschreibung des StatusCode
*/
requestMessage: string;
}

View File

@@ -1,36 +0,0 @@
import { PriceDTO } from './price.dto';
import { AvailabilityType } from '../../enums';
export interface AvailabilityDTO {
/**
* Preis (VK)
*/
price: PriceDTO;
/**
* Lieferant
*/
supplier: string;
/**
* Stock Status Code / Meldeschlüssel
*/
ssc: string;
/**
* Verfügbare Menge
*/
qty?: number;
/**
* Voraussichtliches Lieferdatum
*/
at?: Date;
/**
* Availability status
*/
status: AvailabilityType;
}

View File

@@ -1,18 +0,0 @@
// start:ng42.barrel
export * from './article-availability.dto';
export * from './availability.dto';
export * from './item.dto';
export * from './price-value.dto';
export * from './price.dto';
export * from './product.dto';
export * from './shelf-info.dto';
export * from './shop.dto';
export * from './size.dto';
export * from './spec.dto';
export * from './stock-info.dto';
export * from './text.dto';
export * from './translation.dto';
export * from './vat-value.dto';
export * from './weight.dto';
// end:ng42.barrel

View File

@@ -1,69 +0,0 @@
import { ItemType } from '../../enums/item-type.enum';
import { ProductDTO } from './product.dto';
import { SpecDTO } from './spec.dto';
import { TextDTO } from './text.dto';
import { AvailabilityDTO } from './availability.dto';
import { StockInfoDTO } from './stock-info.dto';
import { ShelfInfoDTO } from './shelf-info.dto';
export interface ItemDTO {
/**
* Item Id
*/
id?: number;
/**
* Rank
*/
ra?: number;
/**
* Artikel / Produkttyp
*/
tp?: ItemType;
/**
* Produkt-Stammdaten
*/
pr: ProductDTO;
/**
* Specs / Technische Daten / Werte
*/
sp: SpecDTO[];
/**
* Texte
*/
te: TextDTO[];
/**
* Verfügbarkeit
*/
av: AvailabilityDTO[];
/**
* Bestandsinformationen
*/
st: StockInfoDTO[];
/**
* Platzierungs-/Regalinformationen
*/
sh: ShelfInfoDTO[];
/**
* Nachfolgeartikel
*/
su: ItemDTO;
/**
* Produktfamilie
*/
fa: ItemDTO;
/**
* Recommendation
*/
recommendation?: boolean;
}

View File

@@ -1,16 +0,0 @@
export interface PriceValueDTO {
/**
* Wert
*/
value: number;
/**
* Währung - ISO 4217
*/
currency: string;
/**
* Währungssymbol
*/
currencySymbol: string;
}

View File

@@ -1,16 +0,0 @@
import { VatValueDTO } from './vat-value.dto';
import { PriceValueDTO } from './price-value.dto';
export interface PriceDTO {
/**
* Preis
*/
value: PriceValueDTO;
/**
* MwSt
*/
vat: VatValueDTO;
}

View File

@@ -1,94 +0,0 @@
import { Avoirdupois } from '../../enums/avoirdupois.enum';
import { SizeDTO } from './size.dto';
import { WeightDTO } from './weight.dto';
export interface ProductDTO {
/**
* Hersteller
*/
manufacturer: string;
/**
* Language / Region
*/
locale: string;
/**
* Format Details
*/
formatDetail: string;
/**
* Format
*/
format: string;
/**
* Reihe
*/
serial: string;
/**
* Band
*/
volume: string;
/**
* Auflage
*/
edition: string;
/**
* Warengruppe
*/
productGroup: string;
/**
* Erscheinungsdatum
*/
publicationDate?: Date;
/**
* Gewicht
*/
weight?: WeightDTO<Avoirdupois>;
/**
* Beitragende, z.B. Autoren, Übersetzer, Sprecher, ...
*/
contributors: string;
/**
* Katalog Produktnr.
*/
catalogProductNumber: string;
/**
* Lieferant Produktnr.
*/
supplierProductNumber: string;
/**
* EAN
*/
ean: string;
/**
* Zusatzname
*/
additionalName: string;
/**
* Name
*/
name: string;
/**
* Größe
*/
size?: SizeDTO<string>;
}

View File

@@ -1,4 +0,0 @@
export interface ShelfInfoDTO {
label: string;
assortment: string;
}

View File

@@ -1,11 +0,0 @@
export interface ShopDTO {
/**
* PK
*/
id?: number;
/**
* Name / Bezeichner
*/
name: string;
}

View File

@@ -1,6 +0,0 @@
export interface SizeDTO<T> {
height: number;
width: number;
length: number;
unit: T;
}

View File

@@ -1,21 +0,0 @@
export interface SpecDTO {
/**
* PK
*/
id: number;
/**
* Key
*/
key: string;
/**
* Label
*/
label: string;
/**
* Wert
*/
value: string;
}

View File

@@ -1,16 +0,0 @@
export interface StockInfoDTO {
/**
* PK
*/
id: number;
/**
* Lagerbestand
*/
inStock: number;
/**
* Dach / Kammer
*/
compartment: string;
}

View File

@@ -1,21 +0,0 @@
export class TextDTO {
/**
* PK
*/
id: number;
/**
* Key
*/
key: string;
/**
* Label
*/
label: string;
/**
* Wert
*/
value: string;
}

View File

@@ -1,6 +0,0 @@
import { StringDictionary } from '../../models';
export interface TranslationDTO {
target: string;
values: StringDictionary<string>;
}

View File

@@ -1,23 +0,0 @@
import { VatType } from '../../enums/vat-type.enum';
export interface VatValueDTO {
/**
* Bezeichner
*/
label: string;
/**
* MwSt.-Satz in Prozent (0-100)
*/
inPercent?: number;
/**
* MwSt.-Satz
*/
vatType: VatType;
/**
* Wert
*/
value?: number;
}

View File

@@ -1,4 +0,0 @@
export interface WeightDTO<T> {
value: number;
unit: T;
}

View File

@@ -1,6 +0,0 @@
export * from './cat-search-settings.dto,';
export * from './order-by.dto';
export * from './sort-value.dto';
export * from './data';
export * from './query';
export * from './ui';

View File

@@ -1,16 +0,0 @@
export interface OrderByDTO {
/**
* Label
*/
label: string;
/**
* Wert
*/
by: string;
/**
* Absteigend
*/
desc?: boolean;
}

View File

@@ -1,29 +0,0 @@
import { CatalogType } from '../../enums';
import { StringDictionary } from '../../models';
export interface AutocompleteTokenDTO {
/**
* Eingabe
*/
input: string;
/**
* Typ (z.B. qs, contributors, title)
*/
type?: string;
/**
* Katalogbereich
*/
catalogType?: CatalogType;
/**
* Filter
*/
filter?: StringDictionary<string>;
/**
* Menge angezeigter Treffer
*/
take?: number;
}

View File

@@ -1,58 +0,0 @@
import { PriceDTO } from '../data';
export interface AvailabilityRequestDTO {
/**
* Artikel / Produkt PK
*/
itemId?: string;
/**
* EAN
*/
ean?: string;
/**
* Menge / Stück
*/
qty: number;
/**
* Bestellzeichen
*/
orderCode?: string;
/**
* Lieferant
*/
supplier: string;
/**
* Bestellung vormerken
*/
preBook?: boolean;
/**
* Preis
*/
price?: PriceDTO;
/**
* Stock Status Code
*/
ssc?: string;
/**
* Vsl. Lieferdatum
*/
estimatedShipping?: Date;
/**
* Shop PK
*/
shopId: number;
/**
* Zuordnungs-ID
*/
availabilityReference?: string;
}

View File

@@ -1,6 +0,0 @@
// start:ng42.barrel
export * from './autocomplete-token.dto';
export * from './availability-request.dto';
export * from './query-token.dto';
// end:ng42.barrel

View File

@@ -1,14 +0,0 @@
import { StringDictionary } from '../../models/dictionary.model';
import { SortValueDTO } from '../sort-value.dto';
import { CatalogType } from '../../enums/catalog-type.enum';
export interface QueryTokenDTO {
input: StringDictionary<string>;
engine?: string;
fuzzy?: number;
catalogType?: CatalogType;
filter?: StringDictionary<string>;
sort?: SortValueDTO[];
skip?: number;
take?: number;
}

View File

@@ -1,4 +0,0 @@
export interface SortValueDTO {
by?: string;
asc?: boolean;
}

View File

@@ -1,8 +0,0 @@
// start:ng42.barrel
export * from './input-group.dto';
export * from './input-options.dto';
export * from './input.dto';
export * from './option.dto';
export * from './ui-settings.dto';
// end:ng42.barrel

View File

@@ -1,20 +0,0 @@
import { InputDTO } from './input.dto';
export interface InputGroupDTO {
/**
* Group / ID
*/
group: string;
/**
* Label
*/
label: string;
/**
* Eingabefelder
*/
input: InputDTO[];
}

View File

@@ -1,15 +0,0 @@
import { OptionDTO } from './option.dto';
export interface InputOptionsDTO {
/**
* Maximale Anzahl auswählbarer Elemente (null => alle, 1 = single select)
*/
max?: number;
/**
* Werte
*/
values: OptionDTO[];
}

View File

@@ -1,39 +0,0 @@
import { InputType } from '../../enums';
import { InputOptionsDTO } from './input-options.dto';
export interface InputDTO {
/**
* Kex / ID
*/
key: string;
/**
* Label
*/
label: string;
/**
* Wasserzeichen
*/
placeholder: string;
/**
* Art des Werts
*/
type: InputType;
/**
* Wert
*/
value: string;
/**
* Regex-Überprüfung
*/
constraint: string;
/**
* Auswahl
*/
options: InputOptionsDTO;
}

View File

@@ -1,18 +0,0 @@
export interface OptionDTO {
/**
* Key / ID
*/
key: string;
/**
* Label
*/
label: string;
/**
* Wert
*/
value: string;
}

View File

@@ -1,12 +0,0 @@
import { InputGroupDTO } from './input-group.dto';
import { InputDTO } from './input.dto';
import { OrderByDTO } from '../order-by.dto';
import { TranslationDTO } from '../data';
export interface UISettingsDTO {
imageUrl: string;
input: InputGroupDTO[];
filter: InputDTO[];
orderBy: OrderByDTO[];
translations: TranslationDTO[];
}

View File

@@ -1,34 +0,0 @@
export enum AvailabilityType {
/**
* Value not set
*/
NotSet = 0,
/**
* Not available
*/
NotAvailable = 1,
/**
* Recordable e.g. on notes
*/
PrebookAtBuyer = 2,
/**
* Is marked in the (web-) shop / order system
*/
PrebookAtRetailer = 32,
/**
* Will be ordered from supplier
*/
PrebookAtSupplier = 256,
/**
* Available
*/
Available = 1024,
/**
* Available / production on request
*/
OnDemand = 2048,
/**
* Available only on defined production data
*/
AtProductionDate = 4096
}

View File

@@ -1,77 +0,0 @@
export enum Avoirdupois {
/**
* Wert nicht gesetzt
*/
NotSet = 0,
/**
* Nanogramm (ng)
*/
Nanogramm = 1,
/**
* Mikrogramm (µg)
*/
Mikrogramm = 2,
/**
* Milligramm (mg)
*/
Milligramm = 4,
/**
* Gramm (g)
*/
Gramm = 8,
/**
* Kilogramm (kg)
*/
Kilogramm = 16,
/**
* MetrischeTonne (t)
*/
MetrischeTonne = 32,
/**
* Zentner (Ztr.)
*/
Zentner = 64,
/**
* Doppel-Zentner (dz.)
*/
DoppelZentner = 128,
/**
* Das metrische Karat ist eine Maßeinheit für die Masse von Edelsteinen.
* = 0,2 g
*/
MetrischesKarat = 256,
/**
* Grain (gr.)
* = 0,06479891 g
*/
Grain = 512,
/**
* Dram (dr.)
* = 1,7718451953125 g
*/
Dram = 1024,
/**
* Ounce (oz.)
* = 28,349523125 g
*/
Ounce = 2048,
/**
* Pound (lb.)
* = 453,59237
*/
Pound = 4096
}

View File

@@ -1,6 +0,0 @@
export enum CatalogType {
NotSet = 0,
Local = 1,
Available = 2,
Archived = 4
}

View File

@@ -1,9 +0,0 @@
// start:ng42.barrel
export * from './availability-type.enum';
export * from './avoirdupois.enum';
export * from './catalog-type.enum';
export * from './input-type.enum';
export * from './item-type.enum';
export * from './vat-type.enum';
// end:ng42.barrel

View File

@@ -1,48 +0,0 @@
export enum InputType {
/**
* Wert nicht gesetzt
*/
NotSet = 0,
/**
* Text
*/
Text = 1,
/**
* Bool (True, False)
*/
Bool = 2,
/**
* Drei mögliche Werte (z.B. null, True, False)
*/
TriState = 4,
/**
* Datum
*/
Date = 8,
/**
* Datum und Uhrzeit
*/
DateTime = 16,
/**
* Ganzzahlig
*/
Integer = 32,
/**
* Dezimal
*/
Decimal = 64,
/**
* Zahl
*/
// tslint:disable-next-line:no-bitwise
Number = 32 | 64
}

View File

@@ -1,86 +0,0 @@
export enum ItemType {
/**
* Wert nicht gesetzt
*/
NotSet = 0,
/**
* Einzelartikel
*/
SingleUnit = 1,
/**
* Verkaufseinheit (VE)
* Packung die einzelne Artikel zu einer verkaufsfähigen Einheit zur Abgabe an den Endverbraucher zusammenfasst
*/
SalesUnit = 2,
/**
* Set
* Artikel besteht aus mehreren, auch einzeln verkaufbaren Artikeln, wird aber als Set mit eigenem Preis angeboten.
*/
Set = 4,
/**
* Verpackungseinheit (VPE)
* Einzelartikel aus der Verpackungseinheit werden an den Endkunden verkauft.
* Ggf. kann auch die komplette VPE an den Endkunden verkauft werden.
* Die VPE kann aber nur als Ganzes (vom Lieferanten) bezogen werden
*/
PackagingUnit = 8,
/**
* Konfigurierbarer Artikel (Wert kann nur in Kombination verwendet werden)
* Anwendbar auf <see cref="SalesUnit"/>, <see cref="Set"/> oder <see cref="PackagingUnit"/>
*/
Configurable = 16,
/**
* Rabatt
*/
Discount = 32,
/**
* Prozentual
* Nur in Kombination mit "Discount" anwendbar
*/
Percentaged = 64,
/**
* Festpreis
* Nur in Kombination mit "Discount" oder "Set" anwendbar
*/
FixedPrice = 128,
/**
* Versandkosten
*/
Postage = 256,
/**
* Bearbeitungsgebühr
*/
HandlingFee = 512,
/**
* Gutschein
*/
Voucher = 1024,
/**
* Bestandteile (z.B. Rezept, Bauteile, ...)
*/
ComponentList = 2048,
/**
* Download
*/
Download = 4096,
/**
* Streaming
*/
Streaming = 8192
}

View File

@@ -1,44 +0,0 @@
export enum VatType {
/**
* Wert nicht gesetzt
*/
NotSet = 0,
/**
* Nullsatz
*/
ZeroRate = 1,
/**
* Standard-Satz
*/
StandardRate = 2,
/**
* Zwischen-Satz
*/
MediumRate = 4,
/**
* Ermäßigter Satz
*/
ReducedRate = 8,
/**
* Stark ermäßigter Satz
*/
VeryReducedRate = 16,
/**
* Dienstleistung
*/
RateForServices = 32,
TaxPaidAtSource = 64,
/**
* Preis enthält mehrere MwSt.-Sätze
*/
MixedVAT = 128
}

View File

@@ -1,14 +0,0 @@
// start:ng42.barrel
export * from './cat-image.service';
export * from './cat-availability.service';
export * from './cat-search-mock.data';
export * from './cat-search-mock.service';
export * from './cat-search.service';
export * from './cat-service.module';
export * from './response';
export * from './tokens';
export * from './dtos';
export * from './enums';
export * from './models';
// end:ng42.barrel

View File

@@ -1,7 +0,0 @@
export interface StringDictionary<T> {
[key: string]: T;
}
export interface NumberDictionary<T> {
[key: number]: T;
}

View File

@@ -1,5 +0,0 @@
// start:ng42.barrel
export * from './dictionary.model';
export * from './valiation-result.model';
// end:ng42.barrel

View File

@@ -1,27 +0,0 @@
import { StringDictionary } from './dictionary.model';
export class ValidationResult {
private _valid = true;
get valid() { return this._valid; }
private _message?: string;
get message() { return this._message; }
private _invalidProperties?: StringDictionary<string>;
get invalidProperties() { return this._invalidProperties; }
setMessage(message: string) {
this._message = message;
this._valid = false;
}
addPropertyError(proeprty: string, reason: string) {
const ip = this.invalidProperties || {};
ip[proeprty] = reason;
this._invalidProperties = ip;
if (this.message == null) {
this.setMessage('Invalid property.');
}
}
}

View File

@@ -1,15 +0,0 @@
import { StringDictionary } from './models/dictionary.model';
export interface ApiResponse<T = void> {
requestId: number;
message: string;
error: boolean;
invalidProperties: StringDictionary<string>;
result: T;
}
export interface PagedApiResponse<T> extends ApiResponse<T[]> {
skip: number;
take: number;
hits: number;
}

View File

@@ -1,5 +0,0 @@
import { InjectionToken } from '@angular/core';
import { Observable } from 'rxjs';
export const CAT_SERVICE_ENDPOINT = new InjectionToken<string | Observable<string>>('cat:service:endpoint');
export const CAT_AV_SERVICE_ENDPOINT = new InjectionToken<string | Observable<string>>('cat:av:service:endpoint');

View File

@@ -1,19 +0,0 @@
import { isNil } from './common';
describe('utils common isNil', () => {
it('should return true if parameter is null', () => {
expect(isNil(null)).toBeTruthy();
});
it('should return true if parameter is undefined', () => {
expect(isNil(undefined)).toBeTruthy();
});
it('should return false if parameter is an empty string', () => {
expect(isNil('')).toBeFalsy();
});
it('should return false if parameter is 0', () => {
expect(isNil(0)).toBeFalsy();
});
});

View File

@@ -1,3 +0,0 @@
export function isNil(val: any): boolean {
return val == null;
}

View File

@@ -1,5 +0,0 @@
// start:ng42.barrel
export * from './common';
export * from './string';
// end:ng42.barrel

View File

@@ -1,23 +0,0 @@
import { stringIsNilOrEmpty } from './string';
describe('utils common stringIsNilOrEmpty', () => {
it('should return true if the parameter is null', () => {
expect(stringIsNilOrEmpty(null)).toBeTruthy();
});
it('should return true if the parameter is undefined', () => {
expect(stringIsNilOrEmpty(undefined)).toBeTruthy();
});
it('should return true if the parameter is an empty string', () => {
expect(stringIsNilOrEmpty('')).toBeTruthy();
});
it('should return true if the parameter is a string with whitespaces', () => {
expect(stringIsNilOrEmpty(' ')).toBeTruthy();
});
it('should return false if the parameter is a string with content', () => {
expect(stringIsNilOrEmpty(' Hello World ')).toBeFalsy();
});
});

View File

@@ -1,10 +0,0 @@
import { isNil } from './common';
export function stringIsNilOrEmpty(value: string) {
if (isNil(value)) {
return true;
} else if (typeof value === 'string' && value.trim() === '') {
return true;
}
return false;
}

View File

@@ -1,28 +0,0 @@
import { availabilityRequestValidator } from './availability-request.validator';
describe('Validators availabilityRequestValidator', () => {
it('should return an invalid ValidationResult when ean and itemId is empty', () => {
const result = availabilityRequestValidator({ qty: 1, shopId: 1, supplier: 'L' });
expect(result.valid).toBeFalsy();
expect(result.invalidProperties['itemId']).toBeDefined();
expect(result.invalidProperties['ean']).toBeDefined();
});
it('should return an invalid ValidationResult when qty is less than 0', () => {
const result = availabilityRequestValidator({ itemId: '123', qty: 0, shopId: 1, supplier: 'L' });
expect(result.valid).toBeFalsy();
expect(result.invalidProperties['qty']).toBeDefined();
});
it('should return an invalid ValidationResult when shopId is less than 0', () => {
const result = availabilityRequestValidator({ ean: '123', qty: 1, shopId: 0, supplier: 'L' });
expect(result.valid).toBeFalsy();
expect(result.invalidProperties['shopId']).toBeDefined();
});
it('should return an invalid ValidationResult when supplier is empty', () => {
const result = availabilityRequestValidator({ itemId: '123', qty: 1, shopId: 1, supplier: '' });
expect(result.valid).toBeFalsy();
expect(result.invalidProperties['supplier']).toBeDefined();
});
});

View File

@@ -1,32 +0,0 @@
import { AvailabilityRequestDTO } from '../dtos';
import { ValidationResult, StringDictionary } from '../models';
import { stringIsNilOrEmpty } from '../utils/string';
import { isNil } from '../utils';
export function availabilityRequestValidator(av: AvailabilityRequestDTO): ValidationResult {
const result = new ValidationResult();
if (isNil(av)) {
result.setMessage('AvailabilityRequestDTO is null or undefined.');
} else {
if (stringIsNilOrEmpty(av.itemId) && stringIsNilOrEmpty(av.ean)) {
result.addPropertyError('itemId', 'Either itemId or ean must be set.');
result.addPropertyError('ean', 'Either itemId or ean must be set.');
}
if (!(av.qty > 0)) {
result.addPropertyError('qty', 'Property qty must be grater than 0.');
}
if (stringIsNilOrEmpty(av.supplier)) {
result.addPropertyError('supplier', 'Property supplier is required.');
}
if (!(av.shopId > 0)) {
result.addPropertyError('shopId', `Value ${av.shopId} is not valid.`);
}
}
return result;
}

View File

@@ -1,4 +0,0 @@
// start:ng42.barrel
export * from './availability-request.validator';
// end:ng42.barrel

View File

@@ -1,14 +0,0 @@
/*
* Public API Surface of cat-service
*/
export * from './lib/cat-image.service';
export * from './lib/cat-availability.service';
export * from './lib/cat-search-mock.data';
export * from './lib/cat-search-mock.service';
export * from './lib/cat-search.service';
export * from './lib/cat-service.module';
export * from './lib/response';
export * from './lib/tokens';
export * from './lib/dtos';
export * from './lib/enums';
export * from './lib/models';

View File

@@ -1,22 +0,0 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@@ -1,32 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@@ -1,17 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@@ -1,17 +0,0 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@@ -1,24 +0,0 @@
# FeedService
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0.
## Code scaffolding
Run `ng generate component component-name --project feed-service` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project feed-service`.
> Note: Don't forget to add `--project feed-service` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build feed-service` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build feed-service`, go to the dist folder `cd dist/feed-service` and run `npm publish`.
## Running unit tests
Run `ng test feed-service` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@@ -1,31 +0,0 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/feed-service'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};

View File

@@ -1,7 +0,0 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/feed-service",
"lib": {
"entryFile": "src/public_api.ts"
}
}

View File

@@ -1,8 +0,0 @@
{
"name": "feed-service",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^7.2.0",
"@angular/core": "^7.2.0"
}
}

View File

@@ -1,13 +0,0 @@
export interface AddressDTO {
city: string;
street: string;
streetNumber: string;
zipCode: string;
country: string;
region: string;
geoLoaction: {
longitude: number;
latitude: number;
altitude: number;
};
}

View File

@@ -1,23 +0,0 @@
import { AddressDTO } from './address-dto';
export interface BranchInfoDTO {
/**
* Branch Id
*/
id: number;
/**
* Branch Name
*/
name: string;
/**
* Branch Key
*/
key: string;
/**
* Address
*/
address: AddressDTO;
}

View File

@@ -1,10 +0,0 @@
export interface EventInfoDTO {
id?: number;
name: string;
desc: string;
image: string;
type: string;
start?: Date;
stop?: Date;
venue: string;
}

Some files were not shown because too many files have changed in this diff Show More