Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • simonrose/naming-backend
  • anderslindh1/naming-backend
  • andersharrisson/naming-backend
3 results
Show changes
Showing
with 1136 additions and 1229 deletions
File added
File added
To do - in particular at time of first set of releases
----------------------------------------------------------------------------------------------------
Content
(a) Before first release
1. verification - static
2. verification - data & application
(b) For first release
1. preparation - close & backup
2. migration scripts - run
3. verification - data & application
4. release - release & deploy
5. post - open & use
(c) For second release
1. preparation - close & backup
2. remove code - remove obsolete
3. migration script - make new, remove previous
4. upgrade database - to latest postgres
5. remove obsolete database content
6. flyway - introduce
7. release - release & deploy
8. post - open & use
----------------------------------------------------------------------------------------------------
Note
- First release
migration of database and functionality
( migration script V6.2 - audit tables contain all )
- Second release
one migration script only
upgrade Postgres database
introduction of Flyway
( possibility to do first and second release together and deploy Naming after second release )
------------------------------------------------------------------------------------------------
- Tests should be able to run at all times
see src/test/resources/INTEGRATIONTEST_DOCKER_RUN.md
To build and run all unit tests and integration tests (Docker) with code coverage.
- Verification of migration with endpoints
/verification - concerns verification of migration up to script V5 (pre audit)
- Configuration not mentioned below
additional configfuration may be required
- Keep migration documentation until judged ok to remove
keep docs/developer/naming_rest_api_migration.doc (.pdf) until some later time
to be able to guide users of old Naming REST API to new Naming REST API
- Respect integrations
----------------------------------------------------------------------------------------------------
(a) Before first release
1. verification - static
1.1 migration scripts
1.2 code
1.3 documentation
2. verification - data & application
2.1 use endpoints
/report
/report/about
/verification
/verification/migration_structures
/verification/migration_names
/verification/data_reachable
/verification/restapi_oldvsnew
2.2 analyse database
example sql queries
2.3 swagger
2.4 other
----------------------------------------------------------------------------------------------------
(b) For first release
1. preparation - close & backup
1.1 close Naming (old)
1.2 backup database
2. migration scripts - run
2.1 run migration script - src/main/resources/db/migration/V4__Schema_data_migration.sql
2.2 run migration script - src/main/resources/db/migration/V5.2__Data_transformation_status_deleted.sql
2.3 run migration script - src/main/resources/db/migration/V6.2__Schema_data_migration_audit.sql
2.7 run migration script - src/main/resources/db/migration/V7__Remove_user_notification_cc_list.sql
about
V4 - migrate to temporary tables, "work in progress"
V5.2 - delete entries that never have been active
V6.2 - migrate to permanent tables, audit tables with all entries
V7 - remove table as no longer used
3. verification - data & application
3.1 use old Naming REST API
3.2 use new Naming REST API
/report
/report/about
/verification
/verification/migration_structures
/verification/migration_names
/verification/data_reachable
/verification/restapi_oldvsnew
swagger
note
that if migration_structures fails, the difference in (# namepartrevision - # sum structures)
should match number of rows that were deleted in script V5.2
3.2 analyse database
example sql queries
3.3 other
4. release - release & deploy
5. post - open & use
4.1 open Naming (new)
----------------------------------------------------------------------------------------------------
(c) For second release
1. preparation - close & backup
1.1 close Naming (new)
1.2 backup database
2. remove code - remove obsolete
2.1. remove code - old and wip (work-in-progress)
src/main/java
org.openepics.names.old.business
org.openepics.names.old.model
org.openepics.names.old.nameviews
org.openepics.names.repository.model.wip
org.openepics.names.repository.old
org.openepics.names.repository.wip
org.openepics.names.rest.beans.old
org.openepics.names.rest.controller.old
org.openepics.names.util.old
org.openepics.names.util.wip
( org.openepics.names.old )
org.openepics.names.rest.controller/ReportController.java
org.openepics.names.rest.controller/VerificationController.java
src/test/java
org.openepics.names.repository.model.wip
org.openepics.names.docker.ReportIT.java
org.openepics.names.docker.VerificationIT.java
old - refers to code for old Naming tool - for reference and verification purposes
wip - refers to work-in-progress (temporary) code for new Naming tool - for migration and verification purposes
2.2. update documentation ( related to removal of code )
src/test/resources
INTEGRATIONTEST_DOCKER_RUN.md
docs/developer
naming_architecture_code.docx
naming_architecture_code.pdf
3. migration script - make new, remove previous
3.1 create with database management tool, e.g. DBeaver
3.2 give name V1__*.sql
3.3 update docker-compose-integrationtest.yml to use new migration script V1__*.sql instead of old migration scripts (V1 - V6)
4. upgrade database - to latest postgres
4.1 decide postgres version to upgrade to, e.g. 16.2
4.2 update docker compose files
docker-compose.yml
docker-compose-demo.yml
docker-compose-integrationtest.yml
5. remove obsolete database content
5.1 remove obsolete tables - tables no longer in use - see 6.2 - decide on flyway table
tables
remove
appinfo
device
devicerevision
( flyway_schema_history )
namepart
namepartrevision
useraccount
wip_devicegroup
wip_devicetype
wip_discipline
wip_name
wip_subsystem
wip_system
wip_systemgroup
keep
audit_name
audit_structure
devicegroup
devicetype
discipline
( flyway_schema_history )
name
subsystem
system
systemgroup
other
related to wip tables -- see also V4__Schema_data_migration.sql
indices
foreign keys
sequences
functions
6. flyway - introduce
6.1 possibly use existing table flyway_schema_history or create a new table
6.2 introduce flyway and make use of new migration script V1__*.sql - see 5.1 - decide on flyway table
6.3 update integration tests accordingly
docker-compose-integrationtest.yml
postgres:
volumes:
- ./src/main/resources/db/migration/V1__*.sql:/docker-entrypoint-initdb.d/V1__*.sql
7. release - release & deploy
8. post - open & use
8.1 open Naming (new)
----------------------------------------------------------------------------------------------------
README
----------
Folder contains content that is useful for naming-backend.
Content
- useful.odg LibreOffice Draw document, content may be exported or copied to image edit tool
and then exported to images that can be put in naming-backend
File added
This diff is collapsed.
SET statement_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET search_path = public, pg_catalog;
SET default_tablespace = '';
SET default_with_oids = false;
--
-- Name: appinfo; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE appinfo (
id bigint NOT NULL,
version integer,
schemaversion integer NOT NULL
);
--
-- Name: appinfo_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE appinfo_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: appinfo_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE appinfo_id_seq OWNED BY appinfo.id;
--
-- Name: device; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE device (
id bigint NOT NULL,
version integer,
uuid character varying(255)
);
--
-- Name: device_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE device_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: device_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE device_id_seq OWNED BY device.id;
--
-- Name: devicerevision; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE devicerevision (
id bigint NOT NULL,
version integer,
additionalinfo character varying(255),
conventionname character varying(255),
conventionnameeqclass character varying(255),
deleted boolean NOT NULL,
instanceindex character varying(255),
requestdate timestamp without time zone,
device_id bigint,
devicetype_id bigint,
requestedby_id bigint,
section_id bigint
);
--
-- Name: devicerevision_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE devicerevision_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: devicerevision_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE devicerevision_id_seq OWNED BY devicerevision.id;
--
-- Name: namepart; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE namepart (
id bigint NOT NULL,
version integer,
nameparttype character varying(255),
uuid character varying(255)
);
--
-- Name: namepart_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE namepart_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: namepart_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE namepart_id_seq OWNED BY namepart.id;
--
-- Name: namepartrevision; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE namepartrevision (
id bigint NOT NULL,
version integer,
deleted boolean NOT NULL,
description character varying(255),
mnemonic character varying(255),
mnemoniceqclass character varying(255),
name character varying(255),
processdate timestamp without time zone,
processorcomment character varying(255),
requestdate timestamp without time zone,
requestercomment character varying(255),
status character varying(255),
namepart_id bigint,
parent_id bigint,
processedby_id bigint,
requestedby_id bigint
);
--
-- Name: namepartrevision_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE namepartrevision_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: namepartrevision_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE namepartrevision_id_seq OWNED BY namepartrevision.id;
--
-- Name: useraccount; Type: TABLE; Schema: public; Owner: -; Tablespace:
--
CREATE TABLE useraccount (
id bigint NOT NULL,
version integer,
role character varying(255),
username character varying(255)
);
--
-- Name: useraccount_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE useraccount_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: useraccount_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE useraccount_id_seq OWNED BY useraccount.id;
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY appinfo ALTER COLUMN id SET DEFAULT nextval('appinfo_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY device ALTER COLUMN id SET DEFAULT nextval('device_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY devicerevision ALTER COLUMN id SET DEFAULT nextval('devicerevision_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepart ALTER COLUMN id SET DEFAULT nextval('namepart_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepartrevision ALTER COLUMN id SET DEFAULT nextval('namepartrevision_id_seq'::regclass);
--
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY useraccount ALTER COLUMN id SET DEFAULT nextval('useraccount_id_seq'::regclass);
--
-- Name: appinfo_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY appinfo
ADD CONSTRAINT appinfo_pkey PRIMARY KEY (id);
--
-- Name: device_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY device
ADD CONSTRAINT device_pkey PRIMARY KEY (id);
--
-- Name: devicerevision_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY devicerevision
ADD CONSTRAINT devicerevision_pkey PRIMARY KEY (id);
--
-- Name: namepart_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY namepart
ADD CONSTRAINT namepart_pkey PRIMARY KEY (id);
--
-- Name: namepartrevision_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY namepartrevision
ADD CONSTRAINT namepartrevision_pkey PRIMARY KEY (id);
--
-- Name: useraccount_pkey; Type: CONSTRAINT; Schema: public; Owner: -; Tablespace:
--
ALTER TABLE ONLY useraccount
ADD CONSTRAINT useraccount_pkey PRIMARY KEY (id);
--
-- Name: fk_3f26vetemhujfdm9q74ecr2u5; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepartrevision
ADD CONSTRAINT fk_3f26vetemhujfdm9q74ecr2u5 FOREIGN KEY (namepart_id) REFERENCES namepart(id);
--
-- Name: fk_4ucnoos7kd8s1gaqbpwm1xptq; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY devicerevision
ADD CONSTRAINT fk_4ucnoos7kd8s1gaqbpwm1xptq FOREIGN KEY (requestedby_id) REFERENCES useraccount(id);
--
-- Name: fk_9vomfk9x1jow27ifx6xc62c5x; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepartrevision
ADD CONSTRAINT fk_9vomfk9x1jow27ifx6xc62c5x FOREIGN KEY (processedby_id) REFERENCES useraccount(id);
--
-- Name: fk_9xs5oy86lf0j8ukpjokjipeke; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepartrevision
ADD CONSTRAINT fk_9xs5oy86lf0j8ukpjokjipeke FOREIGN KEY (requestedby_id) REFERENCES useraccount(id);
--
-- Name: fk_d3ocbsb4tl4ttnusn98khq148; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY devicerevision
ADD CONSTRAINT fk_d3ocbsb4tl4ttnusn98khq148 FOREIGN KEY (devicetype_id) REFERENCES namepart(id);
--
-- Name: fk_l7kklb4mxixjs27nsso6shone; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY devicerevision
ADD CONSTRAINT fk_l7kklb4mxixjs27nsso6shone FOREIGN KEY (section_id) REFERENCES namepart(id);
--
-- Name: fk_l9r1givkfaiol5or2lnr324xp; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY devicerevision
ADD CONSTRAINT fk_l9r1givkfaiol5or2lnr324xp FOREIGN KEY (device_id) REFERENCES device(id);
--
-- Name: fk_lufxqy46l9eiq55d445rbukag; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY namepartrevision
ADD CONSTRAINT fk_lufxqy46l9eiq55d445rbukag FOREIGN KEY (parent_id) REFERENCES namepart(id);
--
-- Name: public; Type: ACL; Schema: -; Owner: -
--
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM postgres;
GRANT ALL ON SCHEMA public TO postgres;
GRANT ALL ON SCHEMA public TO PUBLIC;
INSERT INTO appinfo (version, schemaversion) VALUES (1,1);
......@@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<version>2.7.18</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.openepics</groupId>
......@@ -15,83 +15,115 @@
<description>Naming backend Spring Boot</description>
<properties>
<java.version>17</java.version>
<jacoco.skip>true</jacoco.skip>
<skipITs>true</skipITs>
<skipITCoverage>true</skipITCoverage>
</properties>
<dependencies>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.0-jre</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>findbugs-annotations</artifactId>
<version>3.0.1</version>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-security</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>io.fusionauth</groupId>
<artifactId>fusionauth-jwt</artifactId>
<version>5.3.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.6</version>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.8.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>0.8.12</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.16.3</version>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.16.3</version>
<version>1.19.8</version>
<scope>test</scope>
</dependency>
</dependencies>
......@@ -101,6 +133,110 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/static/pdfs</outputDirectory>
<resources>
<resource>
<directory>docs/about</directory>
<includes>
<include>naming_rest_api_brief_introduction.pdf</include>
<include>naming_rest_api_cheat_sheet.pdf</include>
<include>naming_rest_api_excel_guide.pdf</include>
<include>naming_rest_api_migration.pdf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<skipITs>${skipITs}</skipITs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<classifier>runtime</classifier>
<outputDirectory>${project.build.directory}/jacoco</outputDirectory>
<destFileName>jacocoagent.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>merge</id>
<phase>verify</phase>
<goals>
<goal>merge</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<includes>
<include>jacoco*.exec</include>
</includes>
</fileSet>
</fileSets>
<destFile>${project.build.directory}/site/jacoco/jacoco.exec</destFile>
</configuration>
</execution>
<execution>
<id>report</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${project.build.directory}/site/jacoco/jacoco.exec</dataFile>
<outputDirectory>${project.build.directory}/site/jacoco</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
......@@ -18,9 +18,24 @@
package org.openepics.names;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
/**
* Starting point of SpringBoot application.
*
* @author Lars Johansson
*/
@SpringBootApplication
public class NamingApplication {
......@@ -28,4 +43,44 @@ public class NamingApplication {
SpringApplication.run(NamingApplication.class, args);
}
@Value("${openapi.externaldocs.description}") String openapiExternaldocsDescription;
@Value("${openapi.externaldocs.url}") String openapiExternaldocsUrl;
@Value("${openapi.info.contact.email}") String openapiInfoContactEmail;
@Value("${openapi.info.contact.name}") String openapiInfoContactName;
@Value("${openapi.info.contact.url}") String openapiInfoContactUrl;
@Value("${openapi.info.description}") String openapiInfoDescription;
@Value("${openapi.info.license.name}") String openapiInfoLicenseName;
@Value("${openapi.info.title}") String openapiInfoTitle;
@Bean
public OpenAPI customOpenAPI(@Value("${app.version}") String appVersion) {
return new OpenAPI()
.externalDocs(new ExternalDocumentation()
.description(openapiExternaldocsDescription)
.url(openapiExternaldocsUrl))
.info(new Info()
.contact(new Contact()
.email(openapiInfoContactEmail)
.name(openapiInfoContactName)
.url(openapiInfoContactUrl))
.description(openapiInfoDescription)
.license(new License().name(openapiInfoLicenseName))
.title(openapiInfoTitle)
.version(appVersion));
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE");
}
};
}
}
/*
* Copyright (C) 2023 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.configuration;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
/**
* Set up email configuration for Naming backend.
*
* @author Lars Johansson
*/
@Configuration
public class MailConfiguration {
@Value("${naming.smtp.host}")
String namingSmtpHost;
@Value("${naming.smtp.port}")
int namingSmtpPort;
@Value("${naming.smtp.username}")
String namingSmtpUsername;
@Value("${naming.smtp.password}")
String namingSmtpPassword;
@Value("${spring.mail.properties.mail.smtp.auth}")
String springMailPropertiesMailSmtpAuth;
@Value("${spring.mail.properties.mail.smtp.starttls.enable}")
String springMailPropertiesMailSmtpStarttlsEnable;
@Value("${spring.mail.properties.mail.smtp.connectiontimeout}")
String springMailPropertiesMailSmtpConnectiontimeout;
@Value("${spring.mail.properties.mail.smtp.timeout}")
String springMailPropertiesMailSmtpTimeout;
@Value("${spring.mail.properties.mail.smtp.writetimeout}")
String springMailPropertiesMailSmtpWritetimeout;
@Value("${spring.mail.debug}")
String springMailDebug;
/**
* Set up email configuration with sender.
*/
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(namingSmtpHost);
mailSender.setPort(namingSmtpPort);
mailSender.setUsername(namingSmtpUsername);
mailSender.setPassword(namingSmtpPassword);
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", springMailPropertiesMailSmtpAuth);
props.put("mail.smtp.starttls.enable", springMailPropertiesMailSmtpStarttlsEnable);
props.put("mail.smtp.connectiontimeout", springMailPropertiesMailSmtpConnectiontimeout);
props.put("mail.smtp.timeout", springMailPropertiesMailSmtpTimeout);
props.put("mail.smtp.writetimeout", springMailPropertiesMailSmtpTimeout);
props.put("mail.debug", springMailDebug);
return mailSender;
}
}
/*
* Copyright (C) 2024 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.configuration;
import java.util.Arrays;
import java.util.List;
import org.openepics.names.rest.filter.JwtRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import com.google.common.collect.ImmutableList;
/**
* Set up security configuration for Naming backend.
*
* @author Lars Johansson
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = false)
public class SecurityConfiguration {
// note
// ability to run application with and without security
// spring and swagger work together
// spring
// @EnableMethodSecurity
// @PreAuthorize
// +
// AuthorizationManagerBeforeMethodInterceptor
// SecurityFilterChain
// swagger
// @SecurityRequirement
private static final String API_V1_AUTHENTICATION = "/api/v1/authentication";
private static final String API_V1_NAMES = "/api/v1/names";
private static final String API_V1_NAMES_PATHS = "/api/v1/names/*";
private static final String API_V1_STRUCTURES = "/api/v1/structures";
private static final String API_V1_STRUCTURES_PATHS = "/api/v1/structures/*";
private static final String API_V1_PV_NAMES = "/api/v1/pvNames";
private static final String API_V1_CONVERT = "/api/v1/convert";
private static final String ACTUATOR = "/actuator";
private static final String ACTUATOR_PATHS = "/actuator/**";
private static final String HEALTHCHECK = "/healthcheck";
private static final String REPORT_PATHS = "/report/**";
public static final String ROLE_ADMINISTRATOR = "NamingAdministrator";
public static final String ROLE_USER = "NamingUser";
public static final String IS_ADMINISTRATOR = "hasAuthority('" + ROLE_ADMINISTRATOR + "')";
public static final String IS_USER = "hasAuthority('" + ROLE_USER + "')";
public static final String IS_ADMINISTRATOR_OR_USER = IS_ADMINISTRATOR + " or " + IS_USER;
private static final List<String> ALLOWED_ROLES_TO_LOGIN =
Arrays.asList(
SecurityConfiguration.ROLE_ADMINISTRATOR,
SecurityConfiguration.ROLE_USER);
@Value("${naming.security.enabled:false}")
private boolean namingSecurityEnabled;
@Value("${springdoc.api-docs.path}")
private String apiDocsPath;
@Value("${springdoc.swagger-ui.path}")
private String swaggerUiPath;
private final UserDetailsService jwtUserDetailsService;
private final JwtRequestFilter jwtRequestFilter;
@Autowired
public SecurityConfiguration(
UserDetailsService jwtUserDetailsService,
JwtRequestFilter jwtRequestFilter) {
this.jwtUserDetailsService = jwtUserDetailsService;
this.jwtRequestFilter = jwtRequestFilter;
}
public static List<String> getAllowedRolesToLogin() {
return ImmutableList.copyOf(ALLOWED_ROLES_TO_LOGIN);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnProperty(name = "naming.security.enabled", havingValue = "true")
static AuthorizationManagerBeforeMethodInterceptor preAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// disable csrf since internal
// authentication and authorization
// create, read, update, delete
// not needed for read but needed for others, user for names, admin for structures
// accept requests to
// openapi, swagger
// healthcheck
// login, logout
// patterns that are not mentioned are to be authenticated
// stateless session as session not used to store user's state
// TODO naming-convention-tool
// about
// paths to be removed when such functionality is removed
// /verification to verify data migration
// /rest rest api for comparison
// paths
// /verification/**
// /rest/deviceNames/**
// /rest/healthcheck
// /rest/history/**
// /rest/parts/**
http
.csrf().disable()
.cors();
if (namingSecurityEnabled) {
http
.authorizeRequests()
// api docs, swagger
// authentication - login, logout
// names - read
// structures - read
// other
.mvcMatchers("/v3/api-docs/**", apiDocsPath + "/**", "/api/swagger-ui/**", "/swagger-ui/**", swaggerUiPath).permitAll()
.mvcMatchers(HttpMethod.POST, API_V1_AUTHENTICATION + "/login").permitAll()
.mvcMatchers(HttpMethod.GET, API_V1_NAMES, API_V1_NAMES_PATHS).permitAll()
.mvcMatchers(HttpMethod.GET, API_V1_STRUCTURES, API_V1_STRUCTURES_PATHS).permitAll()
.mvcMatchers(HttpMethod.GET, API_V1_PV_NAMES).permitAll()
.mvcMatchers(HttpMethod.GET, API_V1_CONVERT).permitAll()
.mvcMatchers(HttpMethod.GET, REPORT_PATHS).permitAll()
.mvcMatchers(HttpMethod.GET, HEALTHCHECK).permitAll()
.mvcMatchers(HttpMethod.GET, ACTUATOR, ACTUATOR_PATHS).permitAll()
// naming-convention-tool
.mvcMatchers(HttpMethod.GET, "/verification/**").permitAll()
.mvcMatchers(HttpMethod.GET, "/rest/deviceNames/**").permitAll()
.mvcMatchers(HttpMethod.GET, "/rest/healthcheck").permitAll()
.mvcMatchers(HttpMethod.GET, "/rest/history/**").permitAll()
.mvcMatchers(HttpMethod.GET, "/rest/parts/**").permitAll()
// any other requests to be authenticated
.anyRequest().authenticated()
// add a filter to validate the tokens with every request
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
// make sure we use stateless session; session won't be used to store user's state.
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
return http.build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
// allow % that is URL encoded %25 may be in path
// see org.springframework.security.web.firewall.StrictHttpFirewall
return web -> web.httpFirewall(allowUrlEncodedPercenthHttpFirewall());
}
private HttpFirewall allowUrlEncodedPercenthHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedPercent(true);
return firewall;
}
}
/*
* Copyright (C) 2024 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.configuration;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
/**
* Set up Swagger configuration for Naming backend.
*
* @author Lars Johansson
*/
@Configuration
@SecuritySchemes(value = {
@SecurityScheme(
name = "bearerAuth",
type = SecuritySchemeType.HTTP,
in = SecuritySchemeIn.HEADER,
bearerFormat = "JWT",
scheme = "bearer")
})
public class SwaggerConfiguration {
@Bean
public GroupedOpenApi v1Api() {
return GroupedOpenApi.builder().group("api-v1").pathsToMatch("/api/v1/**").build();
}
@Bean
public GroupedOpenApi base() {
return GroupedOpenApi.builder().group("base").pathsToMatch("/report/**", "/healthcheck").build();
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataConflictException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = 717292299575574858L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataConflictException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataDeletedException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = 8214965988959148282L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataDeletedException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataExistException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = -3192828921283195697L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataExistException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataNotAvailableException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = 771292348691428745L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataNotAvailableException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataNotCorrectException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = 3525813299369253195L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataNotCorrectException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataNotFoundException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = -3908854009422708172L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataNotFoundException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class DataNotValidException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = -1203854671942406300L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public DataNotValidException(String message, String details, String field) {
super(message, details, field, null);
}
}
/*
* Copyright (C) 2022 European Spallation Source ERIC.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.openepics.names.exception;
/**
* Exception class to assist in handling of service layer exceptions.
*
* @author Lars Johansson
*/
public class InputNotAvailableException extends ServiceException {
/**
*
*/
private static final long serialVersionUID = 2223786874114860161L;
/**
* Public constructor.
*
* @param message message
* @param details details
* @param field field
*/
public InputNotAvailableException(String message, String details, String field) {
super(message, details, field, null);
}
}