diff --git a/build.gradle b/build.gradle
index 36ec6d8..64064f3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ allprojects {
apply plugin: 'java'
- version = '0.1.2'
+ version = '0.1.3'
group = 'net.hobbyscience.housedb'
repositories {
diff --git a/database/src/main/java/db/migration/units/R__unit_conversions.java b/database/src/main/java/db/migration/java/units/R__unit_conversions.java
similarity index 99%
rename from database/src/main/java/db/migration/units/R__unit_conversions.java
rename to database/src/main/java/db/migration/java/units/R__unit_conversions.java
index 46f2838..530adfb 100644
--- a/database/src/main/java/db/migration/units/R__unit_conversions.java
+++ b/database/src/main/java/db/migration/java/units/R__unit_conversions.java
@@ -1,4 +1,4 @@
-package db.migration.units;
+package db.migration.java.units;
import java.io.BufferedReader;
import java.io.InputStream;
diff --git a/database/src/main/resources/db/migration/Locations/R__a_locations.sql b/database/src/main/resources/db/migration/Locations/R__a_locations.sql
index d492f3f..4eac9c6 100644
--- a/database/src/main/resources/db/migration/Locations/R__a_locations.sql
+++ b/database/src/main/resources/db/migration/Locations/R__a_locations.sql
@@ -50,10 +50,19 @@ BEGIN
foreach cur_level in ARRAY parts
loop
-- search for existing object at this level
- --raise info 'Search for (%,%)', cur_level,the_parent_id;
- select into the_id,found_parent_id id,parent_id from locations where name=cur_level and (parent_id = the_parent_id); -- or parent_id is null);
+ raise info 'Search for (%,%)', cur_level,the_parent_id;
+ select
+ into the_id,found_parent_id id,parent_id
+ from locations
+ where
+ lower(name)=lower(cur_level)
+ and (
+ (parent_id = the_parent_id)
+ or
+ (parent_id is null and the_parent_id is null)
+ );
if the_id is NULL THEN
- --raise notice 'insert new value';
+ raise notice 'insert new value';
--raise notice '%',found_parent_id ;
insert into locations(name,parent_id) values (cur_level,the_parent_id) returning id into the_id;
the_parent_id = the_id;
@@ -65,4 +74,51 @@ BEGIN
end loop;
RETURN the_id;
END;
-$$ LANGUAGE plpgsql;
\ No newline at end of file
+$$ LANGUAGE plpgsql;
+
+
+create or replace function housedb_timeseries.insert_location()
+returns trigger
+as $$
+declare
+ loc_info housedb.timeseries%rowtype;
+ l_loc_id bigint;
+ l_parent_id bigint;
+ loc_name text;
+begin
+ set search_path to housedb_locations,housedb_units,housedb,public;
+ if TG_OP = 'DELETE' then
+ raise notice 'deleting %', OLD;
+ return OLD;
+ else
+ --raise notice 'Inserting or updating value %', NEW;
+ if NEW.id is not null and NEW.name is not null THEN
+ raise exception 'Specify only timeseries_id or name, not both' using ERRCODE = 'ZX082';
+ end if;
+
+ if TG_OP = 'UPDATE' then
+ perform 'housedb_security.can_perform(housedb_security.get_session_user(),''UPDATE'',''timeseries'',ts_name)';
+ raise exception 'Update not supported at this time';
+ else -- INSERT
+ perform 'housedb_security.can_perform(housedb_security.get_session_user(),''CREATE'',''timeseries'',ts_name)';
+ select housedb_locations.create_location(NEW.name) into l_loc_id;
+ select into l_parent_id parent_id from housedb.view_locations where lower(name) = lower(NEW.name);
+ NEW.id = l_loc_id;
+ NEW.parent_id = l_parent_id;
+ update locations
+ set latitude = NEW.latitude,
+ longitude = NEW.longitude,
+ horizontal_datum = NEW.horizontal_datum,
+ elevation = NEW.elevation,
+ vertical_datum = NEW.vertical_datum
+ where id = NEW.id;
+ end if ;
+
+
+ end if;
+
+ return new;
+end;
+$$ language plpgsql;
+
+
diff --git a/database/src/main/resources/db/migration/V4/V4.5.1__fix_location_uniqueness.sql b/database/src/main/resources/db/migration/V4/V4.5.1__fix_location_uniqueness.sql
new file mode 100644
index 0000000..22af342
--- /dev/null
+++ b/database/src/main/resources/db/migration/V4/V4.5.1__fix_location_uniqueness.sql
@@ -0,0 +1,4 @@
+drop index housedb.location_names_lower;
+
+create unique index location_names_lower_no_parent on housedb.locations( lower(name) ) where parent_id is null;
+create unique index location_names_lower_has_parent on housedb.locations( lower(name), parent_id ) where parent_id is not null;
\ No newline at end of file
diff --git a/database/src/main/resources/db/migration/V5/V5.0.0__location_coordinates.sql b/database/src/main/resources/db/migration/V5/V5.0.0__location_coordinates.sql
new file mode 100644
index 0000000..21f8bda
--- /dev/null
+++ b/database/src/main/resources/db/migration/V5/V5.0.0__location_coordinates.sql
@@ -0,0 +1,5 @@
+alter table housedb.locations add column latitude double precision;
+alter table housedb.locations add column longitude double precision;
+alter table housedb.locations add column horizontal_datum varchar(50);
+alter table housedb.locations add column elevation double precision;
+alter table housedb.locations add column vertical_datum varchar(50);
\ No newline at end of file
diff --git a/database/src/main/resources/db/migration/Views/R__locations.sql b/database/src/main/resources/db/migration/Views/R__locations.sql
new file mode 100644
index 0000000..39c8772
--- /dev/null
+++ b/database/src/main/resources/db/migration/Views/R__locations.sql
@@ -0,0 +1,20 @@
+create or replace view housedb.view_locations as
+ select id,
+ housedb_locations.expand_location_name(
+ id
+ ) AS name,
+ parent_id,
+ housedb_locations.expand_location_name(
+ parent_id
+ ) AS parent,
+ latitude,
+ longitude,
+ horizontal_datum,
+ elevation,
+ vertical_datum
+ from housedb.locations
+;
+
+drop trigger if exists insert_location_trigger on housedb.view_locations;
+create trigger insert_location_trigger instead of insert or update or delete on housedb.view_locations
+ for each row execute procedure housedb_timeseries.insert_location();
diff --git a/database/src/test/java/db/migration/UnitConversionTest.java b/database/src/test/java/db/migration/UnitConversionTest.java
index 2eda3fc..9e2e39e 100644
--- a/database/src/test/java/db/migration/UnitConversionTest.java
+++ b/database/src/test/java/db/migration/UnitConversionTest.java
@@ -2,7 +2,7 @@
import org.junit.jupiter.api.Test;
-import db.migration.units.R__unit_conversions;
+import db.migration.java.units.R__unit_conversions;
import net.hobbyscience.SimpleInfixCalculator;
import net.hobbyscience.database.Conversion;
import net.hobbyscience.database.Unit;
diff --git a/database/src/test/sql/location_test.sql b/database/src/test/sql/location_test.sql
index db11e01..a067ec2 100644
--- a/database/src/test/sql/location_test.sql
+++ b/database/src/test/sql/location_test.sql
@@ -57,6 +57,43 @@ END;
$$ LANGUAGE plpgsql;
+CREATE OR REPLACE FUNCTION housedb_tests.test_create_with_sub_doesnt_create_duplicate()
+RETURNS SETOF TEXT AS $$
+DECLARE
+ base_name text = 'Test';
+ full_loc text = 'Test-Sub Location';
+ n_rows int;
+BEGIN
+ perform housedb_security.add_permission('guest', 'CREATE', 'locations','.*');
+
+ perform housedb_locations.create_location(base_name);
+ select into n_rows count(*) from housedb.locations;
+ RETURN NEXT ok( n_rows = 1, 'Should only have 1 row');
+
+ perform housedb_locations.create_location(full_loc);
+ select into n_rows count(*) from housedb.locations;
+ RETURN NEXT ok( n_rows = 2, 'Should only have 2 rows, duplicate row created');
+
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION housedb_tests.test_insert_new_locations_into_view()
+RETURNS SETOF TEXT AS $$
+DECLARE
+ n_rows int;
+BEGIN
+ perform housedb_security.add_permission('guest', 'CREATE', 'locations','.*');
+
+ insert into
+ housedb.view_locations(name,latitude,longitude,horizontal_datum)
+ values ('Test',0,3,'RASTER');
+ select into n_rows count(*) from housedb.locations;
+ RETURN NEXT ok( n_rows = 1, 'There should be one row');
+
+END;
+$$ LANGUAGE plpgsql;
+
+
diff --git a/housedb-dao/.classpath b/housedb-dao/.classpath
deleted file mode 100644
index 5785087..0000000
--- a/housedb-dao/.classpath
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Dao.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Dao.java
new file mode 100644
index 0000000..21c7b73
--- /dev/null
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Dao.java
@@ -0,0 +1,23 @@
+package net.hobbyscience.housedb.dao;
+
+import java.util.List;
+import java.util.Optional;
+
+import org.jooq.DSLContext;
+
+public abstract class Dao {
+
+ @SuppressWarnings("unused")
+ protected DSLContext dsl = null;
+
+ public Dao(DSLContext dsl){
+ this.dsl = dsl;
+ }
+
+ public abstract List getAll();
+ public abstract Optional getByUniqueName(String uniqueName);
+
+ public abstract void update(T modified);
+ public abstract void save(T newObj);
+
+}
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/HouseDb.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/HouseDb.java
index 3fa6bb1..4c7ec1a 100644
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/HouseDb.java
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/HouseDb.java
@@ -9,6 +9,9 @@
import java.util.ArrayList;
import static net.hobbyscience.housedb.housedb.Routines.*;
+
+import net.hobbyscience.housedb.dto.Location;
+import net.hobbyscience.housedb.dto.TimeSeries;
import net.hobbyscience.housedb.housedb.tables.*;
import static net.hobbyscience.housedb.housedb.tables.TimeseriesValues.*;
@@ -17,6 +20,7 @@
import static net.hobbyscience.housedb.housedb.tables.Parameters.*;
import static net.hobbyscience.housedb.housedb.tables.Intervals.*;
import net.hobbyscience.housedb.housedb_security.Routines;
+import net.hobbyscience.housedb.types.DataTriple;
import static org.jooq.impl.DSL.*;
@@ -53,6 +57,10 @@ public HouseDb setUsername(String username){
return this;
}
+ public LocationsDao locationDao(){
+ return new LocationsDao(dsl);
+ }
+
public List getAllLocations() throws Exception{
ArrayList locations = new ArrayList<>();
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Location.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Location.java
deleted file mode 100644
index b1f11d4..0000000
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/Location.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package net.hobbyscience.housedb.dao;
-
-import java.util.Objects;
-
-public class Location{
- private String name;
-
- public Location() {
- }
-
- public Location(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Location name(String name) {
- setName(name);
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this)
- return true;
- if (!(o instanceof Location)) {
- return false;
- }
- Location location = (Location) o;
- return Objects.equals(name, location.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(name);
- }
-
- @Override
- public String toString() {
- return "{" +
- " name='" + getName() + "'" +
- "}";
- }
-}
\ No newline at end of file
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/LocationsDao.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/LocationsDao.java
new file mode 100644
index 0000000..97ea8e1
--- /dev/null
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/LocationsDao.java
@@ -0,0 +1,108 @@
+package net.hobbyscience.housedb.dao;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.jooq.DSLContext;
+import org.jooq.impl.DSL;
+
+import net.hobbyscience.housedb.dto.Location;
+
+import static net.hobbyscience.housedb.housedb.tables.ViewLocations.*;
+import static net.hobbyscience.housedb.housedb.tables.Locations.*;
+
+public class LocationsDao extends Dao{
+
+ public LocationsDao(DSLContext dsl) {
+ super(dsl);
+ }
+
+ @Override
+ public List getAll() {
+ var locs = dsl
+ .select()
+ .from(VIEW_LOCATIONS).orderBy(VIEW_LOCATIONS.NAME)
+ .fetch().stream().map( r -> r.into(VIEW_LOCATIONS) )
+ .map( r -> {
+ return new Location (
+ r.getId(),
+ r.getName(),
+ r.getParent(),
+ r.getLatitude(),
+ r.getLongitude(),
+ r.getHorizontalDatum(),
+ r.getElevation(),
+ r.getVerticalDatum()
+ );
+ }).collect(Collectors.toList());
+ return locs;
+ }
+
+ @Override
+ public Optional getByUniqueName(String uniqueName) {
+ var res = dsl
+ .select()
+ .from(VIEW_LOCATIONS)
+ .where(DSL.upper(VIEW_LOCATIONS.NAME).eq(DSL.upper(uniqueName)))
+ .orderBy(VIEW_LOCATIONS.NAME)
+ .fetchOne();
+ if( res == null ){
+ return Optional.ofNullable(null);
+ } else {
+ var r = res.into(VIEW_LOCATIONS);
+ return Optional.of(
+ new Location (
+ r.getId(),
+ r.getName(),
+ r.getParent(),
+ r.getLatitude(),
+ r.getLongitude(),
+ r.getHorizontalDatum(),
+ r.getElevation(),
+ r.getVerticalDatum()
+ )
+ );
+ }
+
+
+ }
+
+ @Override
+ public void update(Location modified) {
+ dsl.update(VIEW_LOCATIONS)
+ .set(VIEW_LOCATIONS.NAME,modified.getName())
+
+ .set(VIEW_LOCATIONS.LATITUDE,modified.getLatitude())
+ .set(VIEW_LOCATIONS.LONGITUDE,modified.getLongitude())
+ .set(VIEW_LOCATIONS.HORIZONTAL_DATUM,modified.getHorizontalDatum())
+
+ .set(VIEW_LOCATIONS.LATITUDE,modified.getElevation())
+ .set(VIEW_LOCATIONS.VERTICAL_DATUM,modified.getVerticalDatum())
+
+ .where(VIEW_LOCATIONS.ID.eq(modified.getId()))
+ .execute();
+ }
+
+ @Override
+ public void save(Location newObj) {
+ dsl.insertInto(VIEW_LOCATIONS)
+ .columns(
+ VIEW_LOCATIONS.NAME,
+ VIEW_LOCATIONS.LATITUDE,
+ VIEW_LOCATIONS.LONGITUDE,
+ VIEW_LOCATIONS.HORIZONTAL_DATUM,
+ VIEW_LOCATIONS.ELEVATION,
+ VIEW_LOCATIONS.VERTICAL_DATUM
+ ).values(
+ newObj.getName(),
+ newObj.getLatitude(),
+ newObj.getLongitude(),
+ newObj.getHorizontalDatum(),
+ newObj.getElevation(),
+ newObj.getVerticalDatum()
+ ).execute();
+
+ }
+
+}
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dto/Location.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dto/Location.java
new file mode 100644
index 0000000..a4fe4d1
--- /dev/null
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/dto/Location.java
@@ -0,0 +1,122 @@
+package net.hobbyscience.housedb.dto;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.*;
+
+
+public class Location{
+ @JsonIgnore
+ private long id;
+ private String name;
+ @JsonIgnore
+ private String parent;
+ private Double latitude;
+ private Double longitude;
+ private String horizontalDatum;
+ private Double elevation;
+ private String verticalDatum;
+
+
+ @JsonCreator
+ public Location(
+ @JsonProperty("name") String name,
+ @JsonProperty("latitude") Double latitude,
+ @JsonProperty("longitude") Double longitude,
+ @JsonProperty("horizontalDatum") String horizontalDatum,
+ @JsonProperty("elevation") Double elevation,
+ @JsonProperty("verticalDatum") String verticalDatum) {
+ this.id = -1;
+ this.name = name;
+ this.parent = null;
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.horizontalDatum = horizontalDatum;
+ this.elevation = elevation;
+ this.verticalDatum = verticalDatum;
+ }
+
+ public Location(
+ long id,
+ String name,
+ String parent,
+ Double latitude,
+ Double longitude,
+ String horizontalDatum,
+ Double elevation,
+ String verticalDatum) {
+ this.id = id;
+ this.name = name;
+ this.parent = parent;
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.horizontalDatum = horizontalDatum;
+ this.elevation = elevation;
+ this.verticalDatum = verticalDatum;
+ }
+
+ @JsonIgnore
+ public long getId(){
+ return this.id;
+ }
+
+ @JsonIgnore
+ public String getParent() {
+ return this.parent;
+ }
+
+ public Double getLatitude() {
+ return this.latitude;
+ }
+
+ public Double getLongitude() {
+ return this.longitude;
+ }
+
+ public String getHorizontalDatum() {
+ return this.horizontalDatum;
+ }
+
+ public Double getElevation() {
+ return this.elevation;
+ }
+
+ public String getVerticalDatum() {
+ return this.verticalDatum;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Location)) {
+ return false;
+ }
+ Location location = (Location) o;
+ return Objects.equals(name, location.name) && Objects.equals(parent, location.parent) && Objects.equals(latitude, location.latitude) && Objects.equals(longitude, location.longitude) && Objects.equals(horizontalDatum, location.horizontalDatum) && Objects.equals(elevation, location.elevation) && Objects.equals(verticalDatum, location.verticalDatum);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, parent, latitude, longitude, horizontalDatum, elevation, verticalDatum);
+ }
+
+
+ @Override
+ public String toString() {
+ return "{" +
+ " name='" + name + "'" +
+ ", parent='" + parent + "'" +
+ ", latitude='" + latitude + "'" +
+ ", longitude='" + longitude + "'" +
+ ", horizontal_datum='" + horizontalDatum + "'" +
+ ", elevation='" + elevation + "'" +
+ ", vertical_datum='" + verticalDatum + "'" +
+ "}";
+ }
+}
\ No newline at end of file
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/TimeSeries.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/dto/TimeSeries.java
similarity index 95%
rename from housedb-dao/src/main/java/net/hobbyscience/housedb/dao/TimeSeries.java
rename to housedb-dao/src/main/java/net/hobbyscience/housedb/dto/TimeSeries.java
index 70ffc1d..18e95f3 100644
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/TimeSeries.java
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/dto/TimeSeries.java
@@ -1,4 +1,4 @@
-package net.hobbyscience.housedb.dao;
+package net.hobbyscience.housedb.dto;
import java.util.List;
@@ -6,6 +6,7 @@
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
+import net.hobbyscience.housedb.types.DataTriple;
/**
*
*/
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTriple.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/DataTriple.java
similarity index 97%
rename from housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTriple.java
rename to housedb-dao/src/main/java/net/hobbyscience/housedb/types/DataTriple.java
index c754465..b1d0b6a 100644
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTriple.java
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/DataTriple.java
@@ -1,4 +1,4 @@
-package net.hobbyscience.housedb.dao;
+package net.hobbyscience.housedb.types;
import java.time.OffsetDateTime;
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleDeserializer.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleDeserializer.java
similarity index 91%
rename from housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleDeserializer.java
rename to housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleDeserializer.java
index b27b115..003bf98 100644
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleDeserializer.java
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleDeserializer.java
@@ -1,4 +1,4 @@
-package net.hobbyscience.housedb.dao;
+package net.hobbyscience.housedb.types.serializers;
import java.io.IOException;
import java.time.OffsetDateTime;
@@ -9,6 +9,8 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import net.hobbyscience.housedb.types.DataTriple;
+
public class DataTripleDeserializer extends StdDeserializer{
public static final Logger logger = Logger.getLogger(DataTripleDeserializer.class.getName());
public DataTripleDeserializer(){
diff --git a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleSerializer.java b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleSerializer.java
similarity index 90%
rename from housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleSerializer.java
rename to housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleSerializer.java
index 52bc1f3..5784031 100644
--- a/housedb-dao/src/main/java/net/hobbyscience/housedb/dao/DataTripleSerializer.java
+++ b/housedb-dao/src/main/java/net/hobbyscience/housedb/types/serializers/DataTripleSerializer.java
@@ -1,4 +1,4 @@
-package net.hobbyscience.housedb.dao;
+package net.hobbyscience.housedb.types.serializers;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
@@ -7,6 +7,8 @@
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+import net.hobbyscience.housedb.types.DataTriple;
+
public class DataTripleSerializer extends StdSerializer{
/**
diff --git a/housedb-dao/src/test/java/net/hobbyscience/housedb/dao/DataTripleTest.java b/housedb-dao/src/test/java/net/hobbyscience/housedb/dao/DataTripleTest.java
index 5b5f266..8e89b57 100644
--- a/housedb-dao/src/test/java/net/hobbyscience/housedb/dao/DataTripleTest.java
+++ b/housedb-dao/src/test/java/net/hobbyscience/housedb/dao/DataTripleTest.java
@@ -2,6 +2,10 @@
import org.junit.jupiter.api.Test;
+import net.hobbyscience.housedb.types.DataTriple;
+import net.hobbyscience.housedb.types.serializers.DataTripleDeserializer;
+import net.hobbyscience.housedb.types.serializers.DataTripleSerializer;
+
import static org.junit.jupiter.api.Assertions.*;
import java.time.OffsetDateTime;
diff --git a/z-api/src/main/java/net/hobbyscience/housedb/api/Entry.java b/z-api/src/main/java/net/hobbyscience/housedb/api/Entry.java
index d67274a..a8ffb74 100644
--- a/z-api/src/main/java/net/hobbyscience/housedb/api/Entry.java
+++ b/z-api/src/main/java/net/hobbyscience/housedb/api/Entry.java
@@ -10,6 +10,9 @@
import net.hobbyscience.housedb.api.controllers.*;
import net.hobbyscience.housedb.dao.*;
import net.hobbyscience.housedb.jackson.*;
+import net.hobbyscience.housedb.types.DataTriple;
+import net.hobbyscience.housedb.types.serializers.DataTripleDeserializer;
+import net.hobbyscience.housedb.types.serializers.DataTripleSerializer;
import org.apache.tomcat.jdbc.pool.DataSource;
import java.util.Base64;
@@ -27,15 +30,24 @@
import io.javalin.plugin.openapi.OpenApiPlugin;
import io.javalin.plugin.openapi.ui.ReDocOptions;
import io.javalin.plugin.openapi.ui.SwaggerOptions;
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityScheme;
+import io.swagger.v3.oas.models.security.SecurityScheme.Type;
public class Entry {
private static final Logger logger = Logger.getLogger(Entry.class.getName());
public static OpenApiPlugin getOpenApiPlugin() {
Info info = new Info().version("1.0").description("HouseDB API");
- OpenApiOptions ops = new OpenApiOptions(info)
- .path("/swagger-docs")
+ Components components = new Components().addSecuritySchemes(
+ "bearerAuth",
+ new SecurityScheme().type(Type.HTTP).scheme("bearer").bearerFormat("JWT")
+ );
+
+ OpenApiOptions ops = new OpenApiOptions( () -> new OpenAPI().components(components).info(info) );
+ ops.path("/swagger-docs")
//.toJsonMapper(JacksonToJsonMapper.INSTANCE)
.swagger(new SwaggerOptions("/swagger-ui"))
.reDoc(new ReDocOptions("/redoc"))
@@ -43,6 +55,7 @@ public static OpenApiPlugin getOpenApiPlugin() {
doc.json("500", ErrorResponse.class);
doc.json("503", ErrorResponse.class);
});
+
return new OpenApiPlugin(ops);
}
@@ -93,6 +106,11 @@ public static void main(String []args) {
logger.log(Level.WARNING,"database error",e);
}
})
+ .exception(NotAuthorized.class, (e, ctx ) -> {
+ logger.warning("Unauthorized access");
+ logger.warning(e.getCause().getMessage());
+ ctx.status(HttpServletResponse.SC_UNAUTHORIZED).json("Unauthorized Access");
+ })
.exception(UnsupportedOperationException.class, (e,ctx) -> {
ctx.status(HttpServletResponse.SC_NOT_IMPLEMENTED);
logger.log(Level.WARNING,"error",e);
@@ -108,17 +126,14 @@ public static void main(String []args) {
var header = ctx.header(Header.AUTHORIZATION);
if( header != null ){
// verification will be handled at the gateway
- //val jwt = Jwts.parserBuilder().build().parseClaimsJws(ctx.header("Authorization"))
- logger.info(header);
+ //val jwt = Jwts.parserBuilder().build().parseClaimsJws(ctx.header("Authorization"))
var parts = header.split("\\\\s+");
- var jwt = parts[parts.length-1].split("\\.");
- logger.info(""+jwt.length);
+ var jwt = parts[parts.length-1].split("\\.");
var jwtClaims = Base64.getDecoder().decode(jwt[1]);
var jsonClaims = om.readTree(jwtClaims);
- //val user = jwt.subject()
- logger.fine(jsonClaims.toString());
+ //val user = jwt.subject()
var user = om.treeToValue(jsonClaims.get("sub"),String.class);
- logger.fine(user);
+ logger.fine(user);
ctx.attribute("username",user);
} else {
ctx.attribute("username","guest");
diff --git a/z-api/src/main/java/net/hobbyscience/housedb/api/NotAuthorized.java b/z-api/src/main/java/net/hobbyscience/housedb/api/NotAuthorized.java
new file mode 100644
index 0000000..93c812c
--- /dev/null
+++ b/z-api/src/main/java/net/hobbyscience/housedb/api/NotAuthorized.java
@@ -0,0 +1,7 @@
+package net.hobbyscience.housedb.api;
+
+public class NotAuthorized extends RuntimeException{
+ public NotAuthorized(String msg, Throwable err ){
+ super(msg,err);
+ }
+}
diff --git a/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/LocationController.java b/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/LocationController.java
index 80f728c..8bf92d2 100644
--- a/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/LocationController.java
+++ b/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/LocationController.java
@@ -3,12 +3,18 @@
import io.javalin.apibuilder.*;
import io.javalin.http.*;
import io.javalin.plugin.openapi.annotations.*;
+import net.hobbyscience.housedb.api.NotAuthorized;
import net.hobbyscience.housedb.dao.*;
+import net.hobbyscience.housedb.dto.Location;
+import java.sql.SQLException;
import java.util.logging.Logger;
+import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
+import org.jooq.exception.DataAccessException;
+
public class LocationController implements CrudHandler {
public static final Logger logger = Logger.getLogger(LocationController.class.getName());
@@ -16,6 +22,9 @@ public class LocationController implements CrudHandler {
tags = {"Locations"},
responses = {
@OpenApiResponse(status="200", content = {@OpenApiContent( from = Location[].class,isArray=true)})
+ },
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
}
)
@Override
@@ -23,7 +32,7 @@ public void getAll( Context ctx){
var ds = ctx.appAttribute(DataSource.class);
try( var conn = ds.getConnection(); ){
var db = new HouseDb(conn,ctx.attribute("username"));
- var locations = db.getAllLocations();
+ var locations = db.locationDao().getAll();
ctx.json(locations);
} catch( Exception err ){
throw new RuntimeException("Error retrieving locations",err);
@@ -31,13 +40,30 @@ public void getAll( Context ctx){
}
@OpenApi(
- tags = {"Locations"},
+ pathParams = {
+ @OpenApiParam(name="location-name",description = "Name of the location you're trying to look up.")
+ },
responses = {
@OpenApiResponse(status="200", content = {@OpenApiContent( from = Location.class)})
- }
+ },
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
+ },
+ tags = {"Locations"}
)
public void getOne( Context ctx, String locationName ){
- throw new UnsupportedOperationException("not implemented yet");
+ var ds = ctx.appAttribute(DataSource.class);
+ try( var conn = ds.getConnection(); ){
+ var db = new HouseDb(conn,ctx.attribute("username"));
+ var loc = db.locationDao().getByUniqueName(locationName);
+ if( loc.isPresent() ){
+ ctx.json(loc.get()).contentType("application/json");
+ } else {
+ ctx.result("Location not found").status(HttpServletResponse.SC_NOT_FOUND);
+ }
+ } catch( SQLException err ){
+ throw new RuntimeException("Database error",err);
+ }
}
@OpenApi(
@@ -45,6 +71,9 @@ public void getOne( Context ctx, String locationName ){
requestBody = @OpenApiRequestBody(content = {@OpenApiContent(from = Location.class)}),
responses = {
@OpenApiResponse(status="200", content = {@OpenApiContent( from = Location.class)})
+ },
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
}
)
public void create(Context ctx) {
@@ -52,19 +81,30 @@ public void create(Context ctx) {
var ds = ctx.appAttribute(DataSource.class);
try( var conn = ds.getConnection(); ){
var db = new HouseDb(conn,ctx.attribute("username"));
- db.saveLocation(loc);
- } catch( Exception err ){
+ db.locationDao().save(loc);
+ } catch (DataAccessException err ){
+ if( err.getLocalizedMessage().contains("no CREATE")){
+ throw new NotAuthorized("Location", err);
+ }
+ }
+ catch( Exception err ){
throw new RuntimeException("Error creating location",err);
}
}
@OpenApi(
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
+ },
tags = {"Locations"}
)
public void update(Context ctx, String locationName){ throw new UnsupportedOperationException("not implemented yet"); }
@OpenApi(
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
+ },
tags = {"Locations"}
)
public void delete(Context ctx, String locationName){ throw new UnsupportedOperationException("not implemented yet"); }
diff --git a/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/TimeSeriesController.java b/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/TimeSeriesController.java
index 347387e..21064cb 100644
--- a/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/TimeSeriesController.java
+++ b/z-api/src/main/java/net/hobbyscience/housedb/api/controllers/TimeSeriesController.java
@@ -3,6 +3,8 @@
import io.javalin.http.*;
import io.javalin.plugin.openapi.annotations.*;
import net.hobbyscience.housedb.dao.*;
+import net.hobbyscience.housedb.dto.TimeSeries;
+
import javax.sql.DataSource;
import org.jooq.exception.*;
import java.time.*;
@@ -42,6 +44,9 @@ public void getAll(Context ctx){
responses = {
@OpenApiResponse(status="200", content = {@OpenApiContent( from = TimeSeries.class)}),
@OpenApiResponse(status="404", content = {@OpenApiContent( from = NotFoundResponse.class)})
+ },
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
}
)
public void getOne(Context ctx, String locationName){
@@ -95,11 +100,17 @@ public void create(Context ctx){
}
@OpenApi(
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
+ },
tags = {"TimeSeries"}
)
public void update(Context ctx, String locationName){ throw new UnsupportedOperationException("not implemented yet"); }
@OpenApi(
+ security = {
+ @OpenApiSecurity(name = "bearerAuth")
+ },
tags = {"TimeSeries"}
)
public void delete(Context ctx , String locationName){ throw new UnsupportedOperationException("not implemented yet"); }