Commit fb9c9efa authored by Valentin Suhnjov's avatar Valentin Suhnjov

Merge branch 'releases/release-0.4.0' into master

parents c5275b0a 78afcb8d
......@@ -6,12 +6,11 @@
<artifactId>browser-backend</artifactId>
<name>Backend</name>
<description>backend</description>
<version>0.3.0</version>
<parent>
<groupId>ee.ria.riha</groupId>
<artifactId>browser</artifactId>
<version>0.2.0</version>
<version>0.4.0</version>
</parent>
<dependencies>
......@@ -21,10 +20,6 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
......@@ -49,15 +44,10 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20160810</version>
</dependency>
<dependency>
<groupId>ee.ria.riha</groupId>
<artifactId>storage-client</artifactId>
<version>0.2.0</version>
<version>0.4.0</version>
</dependency>
</dependencies>
......
......@@ -38,6 +38,7 @@ public class ApplicationProperties {
public static class RemoteApi {
private String producerUrl;
private String approverUrl;
public String getProducerUrl() {
return producerUrl;
......@@ -46,5 +47,13 @@ public class ApplicationProperties {
public void setProducerUrl(String producerUrl) {
this.producerUrl = producerUrl;
}
public String getApproverUrl() {
return approverUrl;
}
public void setApproverUrl(String approverUrl) {
this.approverUrl = approverUrl;
}
}
}
......@@ -2,22 +2,22 @@ package ee.ria.riha.conf;
import ee.ria.riha.storage.util.FilterableArgumentResolver;
import ee.ria.riha.storage.util.PageableArgumentResolver;
import freemarker.core.HTMLOutputFormat;
import freemarker.template.TemplateException;
import org.springframework.boot.autoconfigure.web.ErrorViewResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactory;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
@Configuration
public class MvcConfigurer extends WebMvcConfigurerAdapter {
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
......@@ -26,25 +26,23 @@ public class MvcConfigurer extends WebMvcConfigurerAdapter {
argumentResolvers.add(new FilterableArgumentResolver());
}
@Bean
public ViewResolver viewResolver() {
FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
resolver.setCache(true);
resolver.setPrefix("");
resolver.setSuffix(".html");
resolver.setContentType("text/html; charset=UTF-8");
return resolver;
@Override
public void addFormatters(FormatterRegistry registry) {
super.addFormatters(registry);
registry.addConverter(new UUIDConverter());
}
@Bean
public FreeMarkerConfigurer freemarkerConfig() throws IOException, TemplateException {
FreeMarkerConfigurationFactory factory = new FreeMarkerConfigurationFactory();
factory.setTemplateLoaderPaths("classpath:static", "src/main/resource/templates");
factory.setDefaultEncoding("UTF-8");
FreeMarkerConfigurer result = new FreeMarkerConfigurer();
freemarker.template.Configuration configuration = factory.createConfiguration();
configuration.setOutputFormat(HTMLOutputFormat.INSTANCE);
result.setConfiguration(configuration);
return result;
public ErrorViewResolver supportPathBasedLocationStrategyWithoutHashes() {
return (request, status, model) -> status == HttpStatus.NOT_FOUND
? new ModelAndView("index.html", Collections.emptyMap(), HttpStatus.OK)
: null;
}
static class UUIDConverter implements Converter<String, UUID> {
@Override
public UUID convert(String source) {
return UUID.fromString(source);
}
}
}
\ No newline at end of file
package ee.ria.riha.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRawValue;
import lombok.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
/**
* Represents information system. Holds information system data as JSON.
......@@ -17,7 +18,6 @@ import lombok.*;
public class InfoSystem {
@JsonRawValue
@JsonProperty(value = "description")
private String details;
/**
......
......@@ -3,13 +3,12 @@ package ee.ria.riha.service;
import ee.ria.riha.model.InfoSystem;
import ee.ria.riha.storage.domain.MainResourceRepository;
import ee.ria.riha.storage.domain.model.MainResource;
import ee.ria.riha.storage.util.Filterable;
import ee.ria.riha.storage.util.PageRequest;
import ee.ria.riha.storage.util.Pageable;
import ee.ria.riha.storage.util.PagedResponse;
import ee.ria.riha.storage.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
......@@ -19,25 +18,36 @@ import java.util.stream.Collectors;
@Service
public class InfoSystemService {
private static final Function<MainResource, InfoSystem> mainResourceToInfoSystem = new Function<MainResource, InfoSystem>() {
@Override
public InfoSystem apply(MainResource mainResource) {
return new InfoSystem(mainResource.getJsonObject().toString());
}
};
private static final Function<MainResource, InfoSystem> MAIN_RESOURCE_TO_INFO_SYSTEM = mainResource -> new InfoSystem(
mainResource.getJson_context());
@Autowired
private MainResourceRepository mainResourceRepository;
public PagedResponse<InfoSystem> list(Pageable pageable, Filterable filterable) {
PagedResponse<MainResource> response = mainResourceRepository.list(pageable, filterable);
PagedResponse<MainResource> pagedResponse = mainResourceRepository.list(pageable, filterable);
return new PagedResponse<>(new PageRequest(response.getPage(), response.getSize()),
response.getTotalElements(),
response.getContent().stream()
.map(mainResourceToInfoSystem)
return new PagedResponse<>(new PageRequest(pagedResponse.getPage(), pagedResponse.getSize()),
pagedResponse.getTotalElements(),
pagedResponse.getContent().stream()
.map(MAIN_RESOURCE_TO_INFO_SYSTEM)
.collect(Collectors.toList())
);
}
public InfoSystem findByUuid(UUID uuid) {
if (uuid == null) {
return null;
}
FilterRequest filter = new FilterRequest("uuid,=," + uuid.toString(), null, null);
List<MainResource> mainResources = mainResourceRepository.find(filter);
if (mainResources.isEmpty()) {
return null;
}
return MAIN_RESOURCE_TO_INFO_SYSTEM.apply(mainResources.get(0));
}
}
......@@ -7,9 +7,12 @@ import ee.ria.riha.storage.util.Pageable;
import ee.ria.riha.storage.util.PagedResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
@RestController
@RequestMapping("/systems")
public class InfoSystemController {
......@@ -22,4 +25,9 @@ public class InfoSystemController {
return infoSystemService.list(pageable, filterable);
}
@GetMapping("/{infoSystemUuid}")
public InfoSystem getByUuid(@PathVariable("infoSystemUuid") UUID uuid) {
return infoSystemService.findByUuid(uuid);
}
}
server.port=8082
browser.storageClient.baseUrl=http://localhost:8080/riha-storage/api
browser.remoteApi.producerUrl=http://localhost:8083
\ No newline at end of file
browser.remoteApi.producerUrl=http://localhost:8083
browser.remoteApi.approverUrl=http://localhost:8084
\ No newline at end of file
......@@ -35,7 +35,9 @@
"node-sass": "^4.5.3",
"rxjs": "^5.1.0",
"zone.js": "^0.8.4",
"ngx-toastr": "5.3.0"
"ngx-toastr": "5.3.0",
"moment": "2.18.1",
"ng2-page-scroll": "4.0.0-beta.9"
},
"devDependencies": {
"@angular/cli": "1.0.4",
......
......@@ -6,12 +6,11 @@
<artifactId>browser-frontend</artifactId>
<name>Frontend</name>
<description>Frontend</description>
<version>0.3.0</version>
<parent>
<groupId>ee.ria.riha</groupId>
<artifactId>browser</artifactId>
<version>0.2.0</version>
<version>0.4.0</version>
</parent>
<properties>
......
import { Component, ChangeDetectionStrategy} from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import { TranslateService } from '@ngx-translate/core';
import { PageScrollConfig } from 'ng2-page-scroll';
@Component({
selector: 'app-root',
......@@ -18,5 +19,7 @@ export class AppComponent {
// 'en' not supported yet
translate.use('et');
PageScrollConfig.defaultDuration = 400;
}
}
......@@ -10,6 +10,7 @@ import { RouterModule, Routes } from '@angular/router';
import { TagInputModule } from 'ng2-tag-input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ToastrModule } from 'ngx-toastr';
import { Ng2PageScrollModule } from 'ng2-page-scroll';
import missingTranslationHandler from './app.missingTranslation';
......@@ -45,6 +46,9 @@ import { ProducerEditDocumentsComponent } from './components/producer-edit/produ
import { ProducerEditLegislationsComponent } from './components/producer-edit/producer-edit-legislations/producer-edit-legislations.component';
import { ProducerDetailsLegislationsComponent } from './components/producer-details/producer-details-legislations/producer-details-legislations.component';
import { AlertComponent } from './components/alert/alert.component';
import { ProducerDetailsIssuesComponent } from './components/producer-details/producer-details-issues/producer-details-issues.component';
import { ApproverAddIssueComponent } from './components/approver-add-issue/approver-add-issue.component';
import { ApproverIssueDetailsComponent } from './components/approver-issue-details/approver-issue-details.component';
export function HttpLoaderFactory(http: Http) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
......@@ -63,6 +67,8 @@ const routes: Routes = [
{ path: 'Systems', component: BrowserListComponent },
{ path: 'Kirjelda', component: ProducerListComponent },
{ path: 'Describe', component: ProducerListComponent },
{ path: 'Infosüsteemid/Vaata/:id', component: ProducerDetailsComponent },
{ path: 'Systems/Vaata/:id', component: ProducerDetailsComponent },
{ path: 'Kirjelda/Vaata/:id', component: ProducerDetailsComponent },
{ path: 'Describe/View/:id', component: ProducerDetailsComponent },
{ path: 'Kirjelda/Muuda/:id', component: ProducerEditComponent },
......@@ -103,7 +109,10 @@ const routes: Routes = [
ProducerEditDocumentsComponent,
ProducerEditLegislationsComponent,
ProducerDetailsLegislationsComponent,
AlertComponent
AlertComponent,
ProducerDetailsIssuesComponent,
ApproverAddIssueComponent,
ApproverIssueDetailsComponent
],
imports: [
BrowserModule,
......@@ -111,6 +120,7 @@ const routes: Routes = [
HttpModule,
TagInputModule,
BrowserAnimationsModule,
Ng2PageScrollModule.forRoot(),
RouterModule.forRoot(routes),
ToastrModule.forRoot(),
TranslateModule.forRoot({
......@@ -126,7 +136,9 @@ const routes: Routes = [
entryComponents: [
ProducerEditObjectsComponent,
ProducerEditDocumentsComponent,
ProducerEditLegislationsComponent
ProducerEditLegislationsComponent,
ApproverAddIssueComponent,
ApproverIssueDetailsComponent
],
bootstrap: [AppComponent],
providers: [JsonDataService, SystemsService, WindowRefService, EnvironmentService, { provide: APP_INITIALIZER, useFactory: setGlobalEnvironment, deps: [EnvironmentService], multi: true }]
......
<section class="col card p-3 main-content">
<div class="panel-action-heading">
Uus arutelu
</div>
<div class="my-1">
<form class="col-12" #addForm="ngForm" (ngSubmit)="onSubmit(addForm)" >
<div class="form-group" [ngClass]="{'has-danger': title.invalid && (title.dirty || title.touched || addForm.submitted)}">
<input id="title" class="form-control col-12"
ngModel
#title="ngModel"
placeholder="lühikirjeldus hinnangust"
name="title" required>
</div>
<div class="form-group" [ngClass]="{'has-danger': issue.invalid && (issue.dirty || issue.touched || addForm.submitted)}">
<textarea id="issue" class="form-control col-12"
ngModel
#issue="ngModel"
placeholder="kommentaar, et selgitada puudusi või täpsustada, mida kontrolliti"
name="comment" required></textarea>
</div>
<div class="actions-bar">
<div class="buttons pull-right">
<button (click)="activeModal.dismiss()" class="btn btn-default pull-right">Katkesta</button>
<button type="submit" class="btn btn-success pull-right">Salvesta</button>
</div>
</div>
</form>
</div>
</section>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ApproverAddIssueComponent } from './approver-add-issue.component';
describe('ApproverAddIssueComponent', () => {
let component: ApproverAddIssueComponent;
let fixture: ComponentFixture<ApproverAddIssueComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ApproverAddIssueComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ApproverAddIssueComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { SystemsService } from '../../services/systems.service';
import { System } from '../../models/system';
import { ToastrService } from 'ngx-toastr';
@Component({
selector: 'app-approver-add-comment',
templateUrl: './approver-add-issue.component.html',
styleUrls: ['./approver-add-issue.component.scss']
})
export class ApproverAddIssueComponent implements OnInit {
@Input() system: System;
onSubmit(f) :void {
if (f.valid){
this.systemsService.addSystemIssue(this.system.details.uuid, f.value).then(
res => {
this.activeModal.close();
},
err => {
this.toastrService.error('Serveri viga! Proovige uuesti!');
})
}
}
constructor(public activeModal: NgbActiveModal,
private systemsService: SystemsService,
private toastrService: ToastrService) { }
ngOnInit() {
}
}
<section class="col card p-3 main-content">
<div class="panel-action-heading">
{{ feedback.title }}
</div>
<div class="my-1">
<div class="table-responsive">
<table class="table table-striped dataTable" width="100%" cellspacing="0">
<tbody>
<tr>
<td>
{{ feedback.dateCreated | date:'y-MM-dd HH:mm:ss'}}
</td>
<td>
{{ feedback.comment }}
</td>
</tr>
<tr *ngFor="let reply of replies">
<td>
{{ reply.dateCreated | date:'y-MM-dd HH:mm:ss'}}
</td>
<td *ngIf="reply.entityType == 'ISSUE_COMMENT'">
{{ reply.comment }}
</td>
<td *ngIf="reply.entityType == 'ISSUE_EVENT'">
<span *ngIf="reply.type == 'CLOSED'">Arutelu on suletud!</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<form class="col-12" #commentForm="ngForm">
<div class="form-group" *ngIf="feedback.status == 'OPEN'" [ngClass]="{'has-danger': comment.invalid && (comment.dirty || comment.touched || comment.submitted)}">
<textarea #comment="ngModel" ngModel name="comment" class="form-control counter-element" rows="10" placeholder="Kirjuta siia enda kommentaar" required></textarea>
</div>
<div class="actions-bar">
<div class="buttons pull-right">
<button (click)="activeModal.dismiss()" class="btn btn-default pull-right">Sulge</button>
<button (click)="markResolved(commentForm)" *ngIf="feedback.status == 'OPEN'" class="btn btn-outline-primary pull-right">Märgi lahendatuks</button>
<button (click)="postReply(commentForm)" *ngIf="feedback.status == 'OPEN'" class="btn btn-primary pull-right">Lisa kommentaar</button>
</div>
</div>
</form>
</div>
</section>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ApproverIssueDetailsComponent } from './approver-issue-details.component';
describe('ApproverIssueDetailsComponent', () => {
let component: ApproverIssueDetailsComponent;
let fixture: ComponentFixture<ApproverIssueDetailsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ApproverIssueDetailsComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ApproverIssueDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
import { Component, OnInit, Input } from '@angular/core';
import { SystemsService } from '../../services/systems.service';
import { ToastrService } from 'ngx-toastr';
import { NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
@Component({
selector: 'app-approver-feedback-details',
templateUrl: './approver-issue-details.component.html',
styleUrls: ['./approver-issue-details.component.scss']
})
export class ApproverIssueDetailsComponent implements OnInit {
@Input() feedback: any;
replies: any[] = [];
refreshReplies(){
this.systemService.getSystemIssueTimeline(this.feedback.infoSystemUuid, this.feedback.id).then(
res => {
this.replies = res.json().content;
});
}
markResolved(f){
//comment can be empty
this.systemService.closeSystemIssue(this.feedback.id, f.value).then(
res => {
this.refreshReplies();
this.toastrService.success('Lahendatud');
this.activeModal.close();
},
err => {
this.toastrService.error('Lahendatuks märkimine ebaõnnestus. Palun proovi uuesti.');
}
)
}
postReply(f){
if (f.valid){
this.systemService.postSystemIssueComment(this.feedback.id, f.value).then(
res => {
f.reset();
this.refreshReplies();
this.toastrService.success('Kommentaar edukalt lisatud.');
},
err => {
this.toastrService.error('Kommentaari lisamine ebaõnnestus. Palun proovi uuesti.');
}
)
}
}
constructor(private systemService: SystemsService,
private toastrService: ToastrService,
public activeModal: NgbActiveModal) {
}
ngOnInit() {
this.refreshReplies();
}
}
......@@ -25,21 +25,19 @@
<thead class="thead-default">
<tr>
<th><button (click)="onSortChange('owner')" class="btn btn-primary btn-sm">Omanik</button></th>
<th><button (click)="onSortChange('short_name')" class="btn btn-primary btn-sm">Lühinimi</button></th>
<th><button (click)="onSortChange('name')" class="btn btn-primary btn-sm">Infosüsteemi nimi</button></th>
<th><button (click)="onSortChange('modified_date')" class="btn btn-primary btn-sm">Viimati muudetud</button></th>
<th><button (click)="onSortChange('status')" class="btn btn-primary btn-sm">Staatus</button></th>
<th><button (click)="onSortChange('approved')" class="btn btn-primary btn-sm">Kooskõlastatud</button></th>
<th><button (click)="onSortChange('approvalStatus')" class="btn btn-primary btn-sm">Kooskõlastamise staatus</button></th>
<th><button (click)="onSortChange('meta.system_status.status')" class="btn btn-primary btn-sm">Staatus</button></th>
<th><button (click)="onSortChange('creation_date')" class="btn btn-primary btn-sm">Viimati muudetud</button></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let system of gridData.content">
<td class="owner">{{system.description.owner.name ? system.description.owner.name : system.description.owner.code}}</td>
<td class="name">{{system.description.name}}</td>
<td class="last-modified text-nowrap">{{system.description.modified_date}}</td>
<td class="status">{{ getSystemStatus(system) }}</td>
<td class="approved text-nowrap">{{system.description.approved}}</td>
<td class="approval-status text-nowrap">{{ getApprovalStatus(system) }}</td>
<td class="owner">{{system.details.owner.name ? system.details.owner.name : system.details.owner.code}}</td>
<td class="name"><a [routerLink]="['/Infosüsteemid/Vaata', system.details.main_resource_id]">{{system.details.short_name}}</a></td>
<td class="name"><a [routerLink]="['/Infosüsteemid/Vaata', system.details.main_resource_id]">{{system.details.name}}</a></td>
<td class="status">{{ getSystemStatusText(system) }}</td>
<td class="last-modified text-nowrap">{{ system.details.creation_date | date:'y-MM-dd HH:mm:ss'}}</td>
</tr>
</tbody>
</table>
......
import { Component, OnInit } from '@angular/core';
import { SystemsService } from '../../services/systems.service';
import { GridData } from '../../models/grid-data';
import { isNumber } from "util";
import { G } from '../../globals/globals';
@Component({
selector: 'app-browser-list',
......@@ -14,7 +14,7 @@ export class BrowserListComponent implements OnInit {
filters: {
owner: string,
name: string
}
};
onPageChange(newPage){
this.gridData.page = newPage - 1;
......@@ -26,20 +26,26 @@ export class BrowserListComponent implements OnInit {
this.getSystems();
}
getSystemStatus(system){
if (system.description.meta && system.description.meta.system_status) {
return system.description.meta.system_status.status;
} else {
return '';
}
}
getApprovalStatus(system){
if (system.description.meta && system.description.meta.approval_status) {
return system.description.meta.approval_status.status;
} else {
return '';
getSystemStatusText(system){
let statusDescription = 'määramata';
if (system.details.meta && system.details.meta.system_status) {
let status = system.details.meta.system_status.status;
switch (status) {
case G.system_status.IN_USE: {
statusDescription = 'kasutusel';
break;
}
case G.system_status.ESTABLISHING: {
statusDescription = 'asutamisel';
break;
}
case G.system_status.FINISHED: {
statusDescription = 'lõpetatud';
break
}
}
}
return statusDescription;
}
getSystems(): void {
......
......@@ -14,7 +14,13 @@
<div class="form-group row" [ngClass]="{'has-danger': shortName.invalid && (shortName.dirty || shortName.touched || addForm.submitted)}">
<label for="short-name-input" class="col-sm-2 col-form-label pr-sm-0 text-sm-right required">Lühinimi:</label>
<div class="col-sm-10">
<input #shortName="ngModel" class="form-control" name="short_name" ngModel type="text" id="short-name-input" placeholder="nt. AARE" required>
<input #shortName="ngModel"
class="form-control"
name="short_name"
pattern="[a-zA-ZÕõÄäÖöÜü0-9.-]*"