diff --git a/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java b/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java index fb36e3dd4..58e1aec84 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/Controllers.java @@ -197,6 +197,8 @@ public final class Controllers { public static final String ALLOW = "allow"; public static final String SOURCE_ID = "source-id"; + public static final String CWMS_OFFICE = "CWMS"; + private static final String DEPRECATED_HEADER = "CWMS-DATA-Format-Deprecated"; private static final String DEPRECATED_TAB = "2024-11-01 TAB is not used often."; private static final String DEPRECATED_CSV = "2024-11-01 CSV is not used often."; diff --git a/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java index d9d91a081..6d39437c5 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/LocationGroupController.java @@ -224,7 +224,8 @@ public void create(@NotNull Context ctx) { } @OpenApi( - description = "Update existing LocationGroup", + description = "Update existing LocationGroup. Allows for renaming group, assigning new locations, " + + "and unassigning all locations from the group.", requestBody = @OpenApiRequestBody( content = { @OpenApiContent(from = LocationGroup.class, type = Formats.JSON) @@ -235,31 +236,32 @@ public void create(@NotNull Context ctx) { + "unassign all existing locations before assigning new locations specified in the content body " + "Default: false"), @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " - + "owning office of the location group to be updated"), + + "office of the user making the request. This is the office that the location, group, and category " + + "belong to. If the group and/or category belong to the CWMS office, this only identifies the location."), }, method = HttpMethod.PATCH, tags = {TAG} ) @Override - public void update(@NotNull Context ctx, String oldGroupId) { + public void update(@NotNull Context ctx, @NotNull String oldGroupId) { try (Timer.Context ignored = markAndTime(CREATE)) { DSLContext dsl = getDslContext(ctx); - String formatHeader = ctx.req.getContentType(); String body = ctx.body(); + String office = requiredParam(ctx, OFFICE); ContentType contentType = Formats.parseHeader(formatHeader, LocationGroup.class); LocationGroup deserialize = Formats.parseContent(contentType, body, LocationGroup.class); boolean replaceAssignedLocs = ctx.queryParamAsClass(REPLACE_ASSIGNED_LOCS, Boolean.class).getOrDefault(false); LocationGroupDao locationGroupDao = new LocationGroupDao(dsl); - if (!oldGroupId.equals(deserialize.getId())) { + if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(deserialize.getId())) { locationGroupDao.renameLocationGroup(oldGroupId, deserialize); } if (replaceAssignedLocs) { - locationGroupDao.unassignAllLocs(deserialize); + locationGroupDao.unassignAllLocs(deserialize, office); } - locationGroupDao.assignLocs(deserialize); + locationGroupDao.assignLocs(deserialize, office); ctx.status(HttpServletResponse.SC_OK); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java index f838babd0..495183dbf 100644 --- a/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java +++ b/cwms-data-api/src/main/java/cwms/cda/api/TimeSeriesGroupController.java @@ -228,7 +228,8 @@ public void create(@NotNull Context ctx) { } @OpenApi( - description = "Update existing TimeSeriesGroup", + description = "Update existing TimeSeriesGroup. Allows for renaming of the group, " + + "assigning new time series, and unassigning all time series from the group.", requestBody = @OpenApiRequestBody( content = { @OpenApiContent(from = TimeSeriesGroup.class, type = Formats.JSON) @@ -239,31 +240,31 @@ public void create(@NotNull Context ctx) { + "unassign all existing time series before assigning new time series specified in the content body " + "Default: false"), @OpenApiParam(name = OFFICE, required = true, description = "Specifies the " - + "owning office of the time series group to be updated"), + + "office of the user making the request. This is the office that the timeseries, group, and category " + + "belong to. If the group and/or category belong to the CWMS office, this only identifies the timeseries."), }, method = HttpMethod.PATCH, tags = {TAG} ) @Override - public void update(@NotNull Context ctx, String oldGroupId) { - + public void update(@NotNull Context ctx, @NotNull String oldGroupId) { try (Timer.Context ignored = markAndTime(CREATE)) { DSLContext dsl = getDslContext(ctx); - String formatHeader = ctx.req.getContentType(); String body = ctx.body(); + String office = requiredParam(ctx, OFFICE); ContentType contentType = Formats.parseHeader(formatHeader, TimeSeriesGroup.class); TimeSeriesGroup deserialize = Formats.parseContent(contentType, body, TimeSeriesGroup.class); boolean replaceAssignedTs = ctx.queryParamAsClass(REPLACE_ASSIGNED_TS, Boolean.class) .getOrDefault(false); TimeSeriesGroupDao timeSeriesGroupDao = new TimeSeriesGroupDao(dsl); - if (!oldGroupId.equals(deserialize.getId())) { + if (!office.equalsIgnoreCase(CWMS_OFFICE) && !oldGroupId.equals(deserialize.getId())) { timeSeriesGroupDao.renameTimeSeriesGroup(oldGroupId, deserialize); } if (replaceAssignedTs) { - timeSeriesGroupDao.unassignAllTs(deserialize); + timeSeriesGroupDao.unassignAllTs(deserialize, office); } - timeSeriesGroupDao.assignTs(deserialize); + timeSeriesGroupDao.assignTs(deserialize, office); ctx.status(HttpServletResponse.SC_OK); } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationGroupDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationGroupDao.java index 64b47e467..bcdc5c2be 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationGroupDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/LocationGroupDao.java @@ -478,15 +478,15 @@ public void delete(String categoryId, String groupId, boolean cascadeDelete, Str * @param group The location group to create. */ public void create(LocationGroup group) { - String office = group.getOfficeId(); + String office = group.getOfficeId(); String categoryId = group.getLocationCategory().getId(); connection(dsl, conn -> { DSLContext dslContext = getDslContext(conn, office); CWMS_LOC_PACKAGE.call_CREATE_LOC_GROUP2(dslContext.configuration(), categoryId, - group.getId(), group.getDescription(), office, group.getSharedLocAliasId(), + group.getId(), group.getDescription(), group.getOfficeId(), group.getSharedLocAliasId(), group.getSharedRefLocationId()); - assignLocs(group); + assignLocs(group, office); }); } @@ -512,8 +512,7 @@ public void renameLocationGroup(String oldGroupId, LocationGroup newGroup) { }); } - public void unassignAllLocs(LocationGroup group) { - String office = group.getOfficeId(); + public void unassignAllLocs(LocationGroup group, String office) { LocationCategory cat = group.getLocationCategory(); connection(dsl, conn -> { DSLContext dslContext = getDslContext(conn, office); @@ -522,7 +521,7 @@ public void unassignAllLocs(LocationGroup group) { }); } - public void assignLocs(LocationGroup group) { + public void assignLocs(LocationGroup group, String office) { List assignedLocations = group.getAssignedLocations(); if (assignedLocations != null) { List collect = assignedLocations.stream() @@ -530,7 +529,6 @@ public void assignLocs(LocationGroup group) { .collect(toList()); LOC_ALIAS_ARRAY3 assignedLocs = new LOC_ALIAS_ARRAY3(collect); - String office = group.getOfficeId(); LocationCategory cat = group.getLocationCategory(); connection(dsl, conn -> { DSLContext dslContext = getDslContext(conn, office); diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java index 79fe3150b..3c06b9682 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/TimeSeriesGroupDao.java @@ -253,12 +253,12 @@ public void create(TimeSeriesGroup group, boolean failIfExists) { group.getId(), group.getDescription(), formatBool(failIfExists), "T", group.getSharedAliasId(), group.getSharedRefTsId(), group.getOfficeId()); - assignTs(configuration,group); + assignTs(configuration,group, group.getOfficeId()); }); } - private void assignTs(Configuration configuration,TimeSeriesGroup group) { + private void assignTs(Configuration configuration,TimeSeriesGroup group, String office) { List assignedTimeSeries = group.getAssignedTimeSeries(); if(assignedTimeSeries != null) { @@ -267,12 +267,12 @@ private void assignTs(Configuration configuration,TimeSeriesGroup group) { .collect(toList()); TS_ALIAS_TAB_T assignedLocs = new TS_ALIAS_TAB_T(collect); CWMS_TS_PACKAGE.call_ASSIGN_TS_GROUPS(configuration, group.getTimeSeriesCategory().getId(), - group.getId(), assignedLocs, group.getOfficeId()); + group.getId(), assignedLocs, office); } } - public void assignTs(TimeSeriesGroup group) { - connection(dsl, c->assignTs(getDslContext(c,group.getOfficeId()).configuration(),group)); + public void assignTs(TimeSeriesGroup group, String office) { + connection(dsl, c->assignTs(getDslContext(c, office).configuration(),group, office)); } private static TS_ALIAS_T convertToTsAliasType(AssignedTimeSeries assignedTimeSeries) { @@ -290,12 +290,12 @@ public void renameTimeSeriesGroup(String oldGroupId, TimeSeriesGroup group) { ); } - public void unassignAllTs(TimeSeriesGroup group) { + public void unassignAllTs(TimeSeriesGroup group, String officeId) { connection(dsl, c -> CWMS_TS_PACKAGE.call_UNASSIGN_TS_GROUP( - getDslContext(c,group.getOfficeId()).configuration(), + getDslContext(c,officeId).configuration(), group.getTimeSeriesCategory().getId(), group.getId(), - null, "T", group.getOfficeId()) + null, "T", officeId) ); } diff --git a/cwms-data-api/src/test/java/cwms/cda/api/LocationGroupControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/LocationGroupControllerTestIT.java index 1c6bb002a..7ba8c5da8 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/LocationGroupControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/LocationGroupControllerTestIT.java @@ -24,10 +24,20 @@ package cwms.cda.api; +import cwms.cda.api.errors.NotFoundException; +import cwms.cda.data.dao.LocationCategoryDao; +import cwms.cda.data.dao.LocationGroupDao; +import fixtures.CwmsDataApiSetupCallback; import fixtures.TestAccounts; import io.restassured.filter.log.LogDetail; -import org.junit.jupiter.api.Disabled; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import mil.army.usace.hec.test.database.CwmsDatabaseContainer; +import org.jooq.Configuration; +import org.jooq.impl.DSL; +import org.jooq.util.oracle.OracleDSL; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -36,32 +46,69 @@ import cwms.cda.data.dto.LocationGroup; import cwms.cda.formatters.ContentType; import cwms.cda.formatters.Formats; +import usace.cwms.db.jooq.codegen.packages.CWMS_ENV_PACKAGE; import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import static cwms.cda.api.Controllers.*; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.*; @Tag("integration") -@Disabled() class LocationGroupControllerTestIT extends DataApiTestIT { + private static final Logger LOGGER = Logger.getLogger(LocationGroupControllerTestIT.class.getName()); + private List groupsToCleanup = new ArrayList<>(); + private List categoriesToCleanup = new ArrayList<>(); + TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + + @AfterEach + void tearDown() throws Exception { + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + + Configuration configuration = OracleDSL.using(c).configuration(); + CWMS_ENV_PACKAGE.call_SET_SESSION_OFFICE_ID(DSL.using(c).configuration(), "SPK"); + LocationCategoryDao locationCategoryDao = new LocationCategoryDao(DSL.using(configuration)); + LocationGroupDao locationGroupDao = new LocationGroupDao(DSL.using(configuration)); + for (LocationGroup group : groupsToCleanup) { + try { + locationGroupDao.unassignAllLocs(group, "SPK"); + if (!group.getOfficeId().equalsIgnoreCase(CWMS_OFFICE) || !group.getId().equalsIgnoreCase("Default")) { + locationGroupDao.delete(group.getLocationCategory().getId(), group.getId(), true, group.getOfficeId()); + } + } catch (NotFoundException e) { + LOGGER.log(Level.CONFIG, String.format("Failed to delete location group: %s", group.getId()), e); + } + } + for (LocationCategory category : categoriesToCleanup) { + try { + locationCategoryDao.delete(category.getId(), true, category.getOfficeId()); + } catch (NotFoundException e) { + LOGGER.log(Level.CONFIG, String.format("Failed to delete location category: %s", category.getId()), e); + } + } + }, CwmsDataApiSetupCallback.getWebUser()); + } @Test void test_getall() throws Exception { - String officeId = "SPK"; String locationId = "LocationGroupTest"; + String officeId = user.getOperatingOffice(); createLocation(locationId, true, officeId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, "AliasId", 1, locationId); LocationCategory cat = new LocationCategory(officeId, "TestCategory", "IntegrationTesting"); - LocationGroup group = new LocationGroup(cat, officeId, LocationGroupControllerTestIT.class.getSimpleName(), "IntegrationTesting", - "sharedLocAliasId", locationId, 123); - List assignedLocations = group.getAssignedLocations(); - assignedLocations.add(new AssignedLocation(locationId, officeId, "AliasId", 1, locationId)); + LocationGroup group = new LocationGroup(new LocationGroup(cat, officeId, LocationGroupControllerTestIT.class.getSimpleName(), "IntegrationTesting", + "sharedLocAliasId", locationId, 123), Collections.singletonList(assignLoc)); ContentType contentType = Formats.parseHeader(Formats.JSON, LocationCategory.class); String categoryXml = Formats.format(contentType, cat); String groupXml = Formats.format(contentType, group); + groupsToCleanup.add(group); + categoriesToCleanup.add(cat); //Create Category given() .log().ifValidationFails(LogDetail.ALL,true) @@ -130,18 +177,18 @@ void test_getall() throws Exception { @Test void test_create_read_delete() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String locationId = "LocationGroupTest"; createLocation(locationId, true, officeId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; - LocationCategory cat = new LocationCategory(officeId, "TestCategory", "IntegrationTesting"); - LocationGroup group = new LocationGroup(cat, officeId, LocationGroupControllerTestIT.class.getSimpleName(), "IntegrationTesting", - "sharedLocAliasId", locationId, 123); - List assignedLocations = group.getAssignedLocations(); - assignedLocations.add(new AssignedLocation(locationId, officeId, "AliasId", 1, locationId)); + LocationCategory cat = new LocationCategory(officeId, "TestCategory2", "IntegrationTesting"); + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, "AliasId", 1, locationId); + LocationGroup group = new LocationGroup(new LocationGroup(cat, officeId, LocationGroupControllerTestIT.class.getSimpleName(), "IntegrationTesting", + "sharedLocAliasId", locationId, 123), Collections.singletonList(assignLoc)); ContentType contentType = Formats.parseHeader(Formats.JSON, LocationCategory.class); String categoryXml = Formats.format(contentType, cat); String groupXml = Formats.format(contentType, group); + groupsToCleanup.add(group); + categoriesToCleanup.add(cat); //Create Category given() .log().ifValidationFails(LogDetail.ALL,true) @@ -217,7 +264,8 @@ void test_create_read_delete() throws Exception { .log().ifValidationFails(LogDetail.ALL,true) .accept(Formats.JSON) .contentType(Formats.JSON) - .queryParam("office", officeId) + .queryParam(OFFICE, officeId) + .queryParam(CATEGORY_ID, group.getLocationCategory().getId()) .when() .redirects().follow(true) .redirects().max(3) @@ -246,17 +294,15 @@ void test_create_read_delete() throws Exception { @Test void test_rename_group() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String locationId = "LocationGroupTest"; createLocation(locationId, true, officeId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, "AliasId", 1, locationId); LocationCategory cat = new LocationCategory(officeId, "test_rename_group", "IntegrationTesting"); - LocationGroup group = new LocationGroup(cat, officeId, "test_rename_group", "IntegrationTesting", - "sharedLocAliasId", locationId, 123); - registerCategory(cat); - registerGroup(group); - List assignedLocations = group.getAssignedLocations(); - assignedLocations.add(new AssignedLocation(locationId, officeId, "AliasId", 1, locationId)); + LocationGroup group = new LocationGroup(new LocationGroup(cat, officeId, "test_rename_group", "IntegrationTesting", + "sharedLocAliasId", locationId, 123), Collections.singletonList(assignLoc)); + groupsToCleanup.add(group); + categoriesToCleanup.add(cat); ContentType contentType = Formats.parseHeader(Formats.JSON, LocationCategory.class); String categoryXml = Formats.format(contentType, cat); String groupXml = Formats.format(contentType, group); @@ -292,7 +338,7 @@ void test_rename_group() throws Exception { .statusCode(is(HttpServletResponse.SC_CREATED)); LocationGroup newGroup = new LocationGroup(cat, officeId, "test_rename_group_new", "IntegrationTesting", "sharedLocAliasId", locationId, 123); - registerGroup(newGroup); + groupsToCleanup.add(newGroup); String newGroupXml = Formats.format(contentType, newGroup); //Rename Group given() @@ -302,7 +348,7 @@ void test_rename_group() throws Exception { .body(newGroupXml) .header("Authorization", user.toHeaderValue()) .header(CATEGORY_ID, group.getLocationCategory().getId()) - //.header(OFFICE, group.getOfficeId()) + .queryParam(OFFICE, group.getOfficeId()) .when() .redirects().follow(true) .redirects().max(3) @@ -338,7 +384,7 @@ void test_rename_group() throws Exception { .accept(Formats.JSON) .contentType(Formats.JSON) .header("Authorization", user.toHeaderValue()) - //.queryParam(OFFICE, officeId) + .queryParam(OFFICE, officeId) .queryParam(CASCADE_DELETE, "true") .when() .redirects().follow(true) @@ -352,20 +398,21 @@ void test_rename_group() throws Exception { @Test void test_add_assigned_locs() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String locationId = "LocationGroupTest"; createLocation(locationId, true, officeId); String locationId2 = "LocationGroupTest2"; createLocation(locationId2, true, officeId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, "AliasId", 1, locationId); LocationCategory cat = new LocationCategory(officeId, "test_add_assigned_locs", "IntegrationTesting"); - LocationGroup group = new LocationGroup(cat, officeId, "test_add_assigned_locs", "IntegrationTesting", - "sharedLocAliasId", locationId, 123); + LocationGroup group = new LocationGroup(new LocationGroup(cat, officeId, "test_add_assigned_locs", "IntegrationTesting", + "sharedLocAliasId", locationId, 123), Collections.singletonList(assignLoc)); List assignedLocations = group.getAssignedLocations(); - assignedLocations.add(new AssignedLocation(locationId, officeId, "AliasId", 1, locationId)); ContentType contentType = Formats.parseHeader(Formats.JSON, LocationCategory.class); String categoryXml = Formats.format(contentType, cat); String groupXml = Formats.format(contentType, group); + groupsToCleanup.add(group); + categoriesToCleanup.add(cat); //Create Category given() .log().ifValidationFails(LogDetail.ALL,true) @@ -398,13 +445,15 @@ void test_add_assigned_locs() throws Exception { .statusCode(is(HttpServletResponse.SC_CREATED)); assignedLocations.clear(); assignedLocations.add(new AssignedLocation(locationId2, officeId, "AliasId2", 2, locationId2)); - groupXml = Formats.format(contentType, group); + LocationGroup newGroup = new LocationGroup(group, assignedLocations); + groupsToCleanup.add(newGroup); + String newGroupJson = Formats.format(contentType, newGroup); //Add Assigned Locs given() .log().ifValidationFails(LogDetail.ALL,true) .accept(Formats.JSON) .contentType(Formats.JSON) - .body(groupXml) + .body(newGroupJson) .header("Authorization", user.toHeaderValue()) .queryParam(CATEGORY_ID, group.getLocationCategory().getId()) .queryParam(REPLACE_ASSIGNED_LOCS, "true") @@ -445,7 +494,7 @@ void test_add_assigned_locs() throws Exception { .contentType(Formats.JSON) .header("Authorization", user.toHeaderValue()) .queryParam(OFFICE, officeId) - .queryParam(CASCADE_DELETE, "true") + .queryParam(CASCADE_DELETE, true) .when() .redirects().follow(true) .redirects().max(3) @@ -455,4 +504,325 @@ void test_add_assigned_locs() throws Exception { .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_NO_CONTENT)); } + + @Test + void test_district_permissions() throws Exception { + String officeId = user.getOperatingOffice(); + String locationId = "LocationGroupTest"; + createLocation(locationId, true, officeId); + + LocationCategory cat = new LocationCategory(officeId, "test_CWMS_permissions", "Loc Group Integration Testing"); + + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, null, null, null); + LocationGroup group = new LocationGroup(cat, officeId, "test_CWMS_permissions", "Loc Group Integration Testing", + null, locationId, null); + LocationGroup newLocGroup = new LocationGroup(new LocationGroup(cat, officeId, "test_new_CWMS_permissions", "Second Loc Group IT", + null, locationId, null), Collections.singletonList(assignLoc)); + groupsToCleanup.add(group); + categoriesToCleanup.add(cat); + groupsToCleanup.add(newLocGroup); + + String catJson = Formats.format(new ContentType(Formats.JSON), cat); + String groupJson = Formats.format(new ContentType(Formats.JSON), group); + String newGroupJson = Formats.format(new ContentType(Formats.JSON), newLocGroup); + + // create a location category + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(catJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(FAIL_IF_EXISTS, false) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/location/category/") + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_CREATED)); + + // create a location group + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(groupJson) + .header("Authorization", user.toHeaderValue()) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/location/group/") + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_CREATED)); + + // patch the location group owned by district office + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(newGroupJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(FAIL_IF_EXISTS, false) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/location/group/" + group.getId()) + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)); + + // get the location group and assert that changes were made + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, officeId) + .queryParam(CATEGORY_ID, newLocGroup.getLocationCategory().getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + newLocGroup.getId()) + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)) + .body("office-id", equalTo(newLocGroup.getOfficeId())) + .body("id", equalTo(newLocGroup.getId())) + .body("description", equalTo(newLocGroup.getDescription())) + .body("assigned-locations[0].location-id", equalTo(locationId)) + .body("assigned-locations[0].alias-id", nullValue()) + .body("assigned-locations[0].ref-location-id", nullValue()); + } + + @Test + void test_CWMS_permissions() throws Exception { + String officeId = user.getOperatingOffice(); + String locationId = "LocationGroupTest"; + createLocation(locationId, true, officeId); + + LocationCategory cat = new LocationCategory(CWMS_OFFICE, "Default", "Default"); + + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, null, null, null); + LocationGroup group = new LocationGroup(cat, CWMS_OFFICE, "Default", "All Locations", + null, null, null); + LocationGroup newLocGroup = new LocationGroup(group, Collections.singletonList(assignLoc)); + + groupsToCleanup.add(group); + + String newGroupJson = Formats.format(new ContentType(Formats.JSON), newLocGroup); + String groupJson = Formats.format(new ContentType(Formats.JSON), group); + + // get the location group and assert that changes were made + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract() + ; + int expectedSize = response.body().jsonPath().getInt("assigned-locations.size()"); + + // patch the location group owned by CWMS office + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(newGroupJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(REPLACE_ASSIGNED_LOCS, true) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/location/group/" + newLocGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // get the location group and assert that changes were made + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + newLocGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("office-id", equalTo(group.getOfficeId())) + .body("id", equalTo(group.getId())) + .body("description", equalTo(newLocGroup.getDescription())) + .body("assigned-locations[0].location-id", equalTo(locationId)) + .body("assigned-locations[0].alias-id", nullValue()) + .body("assigned-locations[0].ref-location-id", nullValue()); + + // patch the location group owned by CWMS office + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(groupJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(REPLACE_ASSIGNED_LOCS, true) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/location/group/" + newLocGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // get the location group and assert that there are no assigned locations + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("assigned-locations.size()", equalTo(expectedSize)); + } + + @Test + void test_CWMS_permissions_with_replacement() throws Exception { + String officeId = user.getOperatingOffice(); + String locationId = "LocationGroupTest"; + createLocation(locationId, true, officeId); + + LocationCategory cat = new LocationCategory(CWMS_OFFICE, "Default", "Default"); + + AssignedLocation assignLoc = new AssignedLocation(locationId, officeId, null, null, null); + LocationGroup group = new LocationGroup(cat, CWMS_OFFICE, "Default", "All Locations", + null, null, null); + LocationGroup newLocGroup = new LocationGroup(group, Collections.singletonList(assignLoc)); + + groupsToCleanup.add(group); + + String newGroupJson = Formats.format(new ContentType(Formats.JSON), newLocGroup); + String groupJson = Formats.format(new ContentType(Formats.JSON), group); + + // get the starting size of the assigned locations + ExtractableResponse response = given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .extract(); + + int expectedSize = response.body().jsonPath().getInt("assigned-locations.size()"); + + // patch the location group owned by CWMS office + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(newGroupJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(REPLACE_ASSIGNED_LOCS, true) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/location/group/" + group.getId()) + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)); + + // get the location group and assert that changes were made + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + group.getId()) + .then() + .assertThat() + .log().ifValidationFails(LogDetail.ALL,true) + .statusCode(is(HttpServletResponse.SC_OK)) + .body("office-id", equalTo(group.getOfficeId())) + .body("id", equalTo(group.getId())) + .body("description", equalTo(newLocGroup.getDescription())) + .body("assigned-locations[0].location-id", equalTo(locationId)) + .body("assigned-locations[0].alias-id", nullValue()) + .body("assigned-locations[0].ref-location-id", nullValue()); + + // patch the location group owned by CWMS office + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .body(groupJson) + .header("Authorization", user.toHeaderValue()) + .queryParam(REPLACE_ASSIGNED_LOCS, true) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/location/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // get the location group and assert that changes were made + given() + .log().ifValidationFails(LogDetail.ALL,true) + .accept(Formats.JSON) + .contentType(Formats.JSON) + .queryParam(OFFICE, CWMS_OFFICE) + .queryParam(CATEGORY_ID, cat.getId()) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/location/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL,true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("assigned-locations.size()", equalTo(expectedSize)); + } } diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java index 3e584fea9..3304137a3 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesGroupControllerTestIT.java @@ -24,17 +24,27 @@ package cwms.cda.api; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import cwms.cda.api.errors.NotFoundException; +import cwms.cda.data.dao.TimeSeriesCategoryDao; +import cwms.cda.data.dao.TimeSeriesDaoImpl; +import cwms.cda.data.dao.TimeSeriesGroupDao; +import cwms.cda.data.dto.TimeSeries; import fixtures.CwmsDataApiSetupCallback; import fixtures.TestAccounts; import io.restassured.filter.log.LogDetail; import io.restassured.path.json.JsonPath; import io.restassured.response.Response; import mil.army.usace.hec.test.database.CwmsDatabaseContainer; +import org.apache.commons.io.IOUtils; import org.hamcrest.Matchers; import org.jooq.Configuration; +import org.jooq.impl.DSL; import org.jooq.util.oracle.OracleDSL; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -47,11 +57,19 @@ import usace.cwms.db.jooq.codegen.packages.CWMS_UTIL_PACKAGE; import javax.servlet.http.HttpServletResponse; +import java.io.InputStream; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import static cwms.cda.api.Controllers.*; +import static cwms.cda.data.dao.JooqDao.formatBool; import static io.restassured.RestAssured.given; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; @@ -59,10 +77,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; @Tag("integration") -@Disabled() // not clearing groups correctly. class TimeSeriesGroupControllerTestIT extends DataApiTestIT { - public static final String CWMS_OFFICE = "CWMS"; + private List categoriesToCleanup = new ArrayList<>(); + private List groupsToCleanup = new ArrayList<>(); + private List timeSeriesToCleanup = new ArrayList<>(); + private static final Logger LOGGER = Logger.getLogger(TimeSeriesGroupControllerTestIT.class.getName()); + TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; @BeforeAll public static void load_data() throws Exception { @@ -79,6 +100,53 @@ public static void load_data() throws Exception { loadSqlDataFromResource("cwms/cda/data/sql/spk_aliases_and_groups.sql"); } + @AfterAll + public static void tear_down() throws Exception { + loadSqlDataFromResource("cwms/cda/data/sql/delete_mixed_ts_group.sql"); + loadSqlDataFromResource("cwms/cda/data/sql/delete_spk_aliases_and_groups.sql"); + } + + @AfterEach + public void clear_data() throws Exception { + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + Configuration configuration = DSL.using(c).configuration(); + TimeSeriesGroupDao groupDao = new TimeSeriesGroupDao(configuration.dsl()); + TimeSeriesCategoryDao categoryDao = new TimeSeriesCategoryDao(configuration.dsl()); + TimeSeriesDaoImpl timeSeriesDao = new TimeSeriesDaoImpl(configuration.dsl()); + + for (TimeSeriesGroup group : groupsToCleanup) { + try { + groupDao.unassignAllTs(group, "SPK"); + if (!group.getOfficeId().equalsIgnoreCase(CWMS_OFFICE)) { + groupDao.delete(group.getTimeSeriesCategory().getId(), group.getId(), group.getOfficeId()); + } + } catch (NotFoundException e) { + LOGGER.log(Level.CONFIG, "Group not found", e); + } + } + for (TimeSeriesCategory category : categoriesToCleanup) { + try { + categoryDao.delete(category.getId(), true, category.getOfficeId()); + } catch (NotFoundException e) { + LOGGER.log(Level.CONFIG, "Category not found", e); + } + } + for (TimeSeries ts : timeSeriesToCleanup) { + try { + timeSeriesDao.delete(ts.getOfficeId(), ts.getName(), new TimeSeriesDaoImpl.DeleteOptions.Builder() + .withStartTimeInclusive(true).withEndTimeInclusive(true).withMaxVersion(false) + .withOverrideProtection(formatBool(true)).build()); + } catch (NotFoundException e) { + LOGGER.log(Level.CONFIG, "Time Series not found", e); + } + } + groupsToCleanup.clear(); + categoriesToCleanup.clear(); + timeSeriesToCleanup.clear(); + }, CwmsDataApiSetupCallback.getWebUser()); + } + @Test void test_group_SPK() { @@ -86,7 +154,7 @@ void test_group_SPK() { given() .log().ifValidationFails(LogDetail.ALL,true) .accept("application/json") - .queryParam("office", "SPK") + .queryParam("office", user.getOperatingOffice()) .when() .get("/timeseries/group") .then() @@ -94,8 +162,8 @@ void test_group_SPK() { .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(200)) .body("$.size()", is(1), - "[0].time-series-category.office-id", is("SPK"), - "[0].office-id", is("SPK")) + "[0].time-series-category.office-id", is(user.getOperatingOffice()), + "[0].office-id", is(user.getOperatingOffice())) .extract() .response(); @@ -132,7 +200,7 @@ void test_group_CWMS() { int itemIndex = ids.indexOf(testGroupId); - assertThat(jsonPathEval.get("[" + itemIndex + "].time-series-category.office-id"), Matchers.is("CWMS")); + assertThat(jsonPathEval.get("[" + itemIndex + "].time-series-category.office-id"), Matchers.is(CWMS_OFFICE)); List tsIds = jsonPathEval.get("[" + itemIndex + "].assigned-time-series.timeseries-id"); assertNotNull(tsIds); @@ -149,11 +217,10 @@ void test_group_CWMS() { @Test void test_create_read_delete() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String timeSeriesId = "Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda"; createLocation(timeSeriesId.split("\\.")[0],true,officeId); createTimeseries(officeId,timeSeriesId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; TimeSeriesCategory cat = new TimeSeriesCategory(officeId, "test_create_read_delete", "IntegrationTesting"); TimeSeriesGroup group = new TimeSeriesGroup(cat, officeId, "test_create_read_delete", "IntegrationTesting", "sharedTsAliasId", timeSeriesId); @@ -172,7 +239,7 @@ void test_create_read_delete() throws Exception { .body(categoryXml) .header("Authorization", user.toHeaderValue()) .queryParam(OFFICE, officeId) - .queryParam(FAIL_IF_EXISTS, "false") + .queryParam(FAIL_IF_EXISTS, false) .when() .redirects().follow(true) .redirects().max(3) @@ -188,7 +255,7 @@ void test_create_read_delete() throws Exception { .contentType(Formats.JSON) .body(groupXml) .header("Authorization", user.toHeaderValue()) - .queryParam(FAIL_IF_EXISTS, "false") + .queryParam(FAIL_IF_EXISTS, false) .when() .redirects().follow(true) .redirects().max(3) @@ -260,7 +327,7 @@ void test_create_read_delete() throws Exception { .log().ifValidationFails(LogDetail.ALL,true) .accept(Formats.JSON) .contentType(Formats.JSON) - .queryParam("office", officeId) + .queryParam(OFFICE, officeId) .when() .redirects().follow(true) .redirects().max(3) @@ -288,7 +355,7 @@ void test_create_read_delete() throws Exception { private static BigDecimal getTsCode(String officeId, String timeSeriesId) throws SQLException { CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); - return db.connection((c) -> { + return db.connection(c -> { Configuration configuration = OracleDSL.using(c).configuration(); BigDecimal officeCode = CWMS_UTIL_PACKAGE.call_GET_OFFICE_CODE(configuration, officeId); return CWMS_TS_PACKAGE.call_GET_TS_CODE(configuration, timeSeriesId, officeCode); @@ -297,11 +364,11 @@ private static BigDecimal getTsCode(String officeId, String timeSeriesId) throws @Test void test_rename_group() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String timeSeriesId = "Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda"; createLocation(timeSeriesId.split("\\.")[0],true,officeId); createTimeseries(officeId, timeSeriesId); - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; + TimeSeriesCategory cat = new TimeSeriesCategory(officeId, "test_rename_group_cat", "IntegrationTesting"); TimeSeriesGroup group = new TimeSeriesGroup(cat, officeId, "test_rename_group", "IntegrationTesting", "sharedTsAliasId", timeSeriesId); @@ -356,7 +423,7 @@ void test_rename_group() throws Exception { .body(newGroupXml) .header("Authorization", user.toHeaderValue()) .header(CATEGORY_ID, group.getTimeSeriesCategory().getId()) - .header(OFFICE, group.getOfficeId()) + .queryParam(OFFICE, group.getOfficeId()) .when() .redirects().follow(true) .redirects().max(3) @@ -396,7 +463,7 @@ void test_rename_group() throws Exception { .body(newGroupXml) .header("Authorization", user.toHeaderValue()) .queryParam(CATEGORY_ID, newGroup.getTimeSeriesCategory().getId()) - .queryParam(REPLACE_ASSIGNED_TS, "true") + .queryParam(REPLACE_ASSIGNED_TS, true) .queryParam(OFFICE, newGroup.getOfficeId()) .when() .redirects().follow(true) @@ -441,9 +508,8 @@ void test_rename_group() throws Exception { @Test void test_add_assigned_locs() throws Exception { - String officeId = "SPK"; + String officeId = user.getOperatingOffice(); String timeSeriesId = "Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda"; - TestAccounts.KeyUser user = TestAccounts.KeyUser.SPK_NORMAL; TimeSeriesCategory cat = new TimeSeriesCategory(officeId, "test_add_assigned_locs", "IntegrationTesting"); TimeSeriesGroup group = new TimeSeriesGroup(cat, officeId, "test_add_assigned_locs", "IntegrationTesting", "sharedTsAliasId", timeSeriesId); @@ -461,7 +527,7 @@ void test_add_assigned_locs() throws Exception { .contentType(Formats.JSON) .body(categoryXml) .header("Authorization", user.toHeaderValue()) - .queryParam(FAIL_IF_EXISTS, "false") + .queryParam(FAIL_IF_EXISTS, false) .queryParam(OFFICE, officeId) .when() .redirects().follow(true) @@ -478,7 +544,7 @@ void test_add_assigned_locs() throws Exception { .contentType(Formats.JSON) .body(groupXml) .header("Authorization", user.toHeaderValue()) - .queryParam(FAIL_IF_EXISTS, "false") + .queryParam(FAIL_IF_EXISTS, false) .when() .redirects().follow(true) .redirects().max(3) @@ -500,7 +566,7 @@ void test_add_assigned_locs() throws Exception { .body(groupXml) .header("Authorization", user.toHeaderValue()) .queryParam(CATEGORY_ID, group.getTimeSeriesCategory().getId()) - .queryParam(REPLACE_ASSIGNED_LOCS, "true") + .queryParam(REPLACE_ASSIGNED_LOCS, true) .queryParam(OFFICE, group.getOfficeId()) .when() .redirects().follow(true) @@ -541,7 +607,7 @@ void test_add_assigned_locs() throws Exception { .body(groupXml) .header("Authorization", user.toHeaderValue()) .queryParam(CATEGORY_ID, group.getTimeSeriesCategory().getId()) - .queryParam(REPLACE_ASSIGNED_TS, "true") + .queryParam(REPLACE_ASSIGNED_TS, true) .queryParam(OFFICE, group.getOfficeId()) .when() .redirects().follow(true) @@ -583,4 +649,308 @@ void test_add_assigned_locs() throws Exception { .log().ifValidationFails(LogDetail.ALL,true) .statusCode(is(HttpServletResponse.SC_NO_CONTENT)); } + + @Test + void test_patch_permissions_CWMS() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + InputStream resource = this.getClass().getResourceAsStream( + "/cwms/cda/api/timeseries_create_SPK.json"); + assertNotNull(resource); + String tsData = IOUtils.toString(resource, StandardCharsets.UTF_8) + .replace("ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST", "ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST4"); + + TimeSeries deserialize = Formats.parseContent(new ContentType(Formats.JSON), tsData, TimeSeries.class); + timeSeriesToCleanup.add(deserialize); + + JsonNode ts = mapper.readTree(tsData); + String location = ts.get("name").asText().split("\\.")[0]; + String officeId = ts.get("office-id").asText(); + createLocation(location, true, officeId); + + // inserting the time series + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .contentType(Formats.JSONV2) + .body(tsData) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/timeseries/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + String categoryName = "Default"; + String groupId = "Default"; + String tsId = ts.get("name").asText(); + TimeSeriesCategory category = new TimeSeriesCategory(CWMS_OFFICE, categoryName, "Default"); + TimeSeriesGroup group = new TimeSeriesGroup(category, CWMS_OFFICE, groupId, "All Time Series", null, null); + AssignedTimeSeries assignedTimeSeries = new AssignedTimeSeries(officeId, tsId, null, null, null, null); + TimeSeriesGroup newGroup = new TimeSeriesGroup(group, Collections.singletonList(assignedTimeSeries)); + + String newGroupJson = Formats.format(new ContentType(Formats.JSONV1), newGroup); + + groupsToCleanup.add(newGroup); + + // Retrieve the group and assert it's empty + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .queryParam(OFFICE, CWMS_OFFICE) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("description", equalTo("All Time Series")) + .body("assigned-time-series.size()", equalTo(0)); + + // Attempt a patch on TS owned by CWMS + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .body(newGroupJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/timeseries/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // Retrieve the group and assert the changes + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .queryParam(OFFICE, CWMS_OFFICE) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + group.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("description", equalTo("All Time Series")) + .body("assigned-time-series.size()", equalTo(1)) + .body("assigned-time-series[0].timeseries-id", equalTo(tsId)); + } + + @Test + void test_patch_permissions_CWMS_with_replacement() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + InputStream resource = this.getClass().getResourceAsStream( + "/cwms/cda/api/timeseries_create_SPK.json"); + assertNotNull(resource); + String tsData = IOUtils.toString(resource, StandardCharsets.UTF_8); + String tsData2 = tsData + .replace("ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST", "ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST2"); + + TimeSeries deserialize = Formats.parseContent(new ContentType(Formats.JSON), tsData, TimeSeries.class); + TimeSeries deserialize2 = Formats.parseContent(new ContentType(Formats.JSON), tsData2, TimeSeries.class); + timeSeriesToCleanup.add(deserialize2); + timeSeriesToCleanup.add(deserialize); + + JsonNode ts = mapper.readTree(tsData); + JsonNode ts2 = mapper.readTree(tsData2); + String location = ts.get("name").asText().split("\\.")[0]; + String officeId = ts.get("office-id").asText(); + createLocation(location, true, officeId); + + // inserting the time series + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .contentType(Formats.JSONV2) + .body(tsData) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/timeseries/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // inserting the time series + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .contentType(Formats.JSONV2) + .body(tsData2) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/timeseries/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + String categoryName = "Default"; + String groupId = "Default"; + String tsId = ts.get("name").asText(); + String tsId2 = ts2.get("name").asText(); + TimeSeriesCategory category = new TimeSeriesCategory(CWMS_OFFICE, categoryName, "Default"); + TimeSeriesGroup group = new TimeSeriesGroup(category, CWMS_OFFICE, groupId, "All Time Series", null, null); + AssignedTimeSeries assignedTimeSeries = new AssignedTimeSeries(officeId, tsId, null, null, null, null); + AssignedTimeSeries assignedTimeSeries2 = new AssignedTimeSeries(officeId, tsId2, null, null, null, null); + TimeSeriesGroup newGroup = new TimeSeriesGroup(group, Arrays.asList(assignedTimeSeries2, assignedTimeSeries)); + + String newGroupJson2 = Formats.format(new ContentType(Formats.JSONV1), newGroup); + + groupsToCleanup.add(newGroup); + + // Attempt a patch on TS owned by CWMS with replacement + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .queryParam(REPLACE_ASSIGNED_TS, true) + .body(newGroupJson2) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/timeseries/group/" + newGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // Retrieve the group and assert the changes + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .queryParam(OFFICE, CWMS_OFFICE) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + newGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("description", equalTo("All Time Series")) + .body("assigned-time-series.size()", equalTo(2)); + } + + @Test + void test_patch_district_permission() throws Exception { + ObjectMapper mapper = new ObjectMapper(); + InputStream resource = this.getClass().getResourceAsStream( + "/cwms/cda/api/timeseries_create_SPK.json"); + assertNotNull(resource); + String tsData = IOUtils.toString(resource, StandardCharsets.UTF_8) + .replace("ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST", "ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST3"); + + TimeSeries deserialize = Formats.parseContent(new ContentType(Formats.JSON), tsData, TimeSeries.class); + timeSeriesToCleanup.add(deserialize); + + JsonNode ts = mapper.readTree(tsData); + String location = ts.get("name").asText().split("\\.")[0]; + String officeId = ts.get("office-id").asText(); + createLocation(location, true, officeId); + String tsId = ts.get("name").asText(); + + TimeSeriesCategory category = new TimeSeriesCategory(CWMS_OFFICE, "Default", "Default"); + TimeSeriesGroup districtGroup = new TimeSeriesGroup(category, CWMS_OFFICE, "Default", "All Time Series", null, null); + AssignedTimeSeries assignedTimeSeries = new AssignedTimeSeries(officeId, tsId, null, null, null, null); + TimeSeriesGroup newDistrictGroup = new TimeSeriesGroup(districtGroup, Collections.singletonList(assignedTimeSeries)); + groupsToCleanup.add(newDistrictGroup); + + String newDistrictGroupJson = Formats.format(new ContentType(Formats.JSONV1), newDistrictGroup); + + // inserting the time series + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV2) + .contentType(Formats.JSONV2) + .body(tsData) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .when() + .redirects().follow(true) + .redirects().max(3) + .post("/timeseries/") + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // Verify the group is empty + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, CWMS_OFFICE) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + newDistrictGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("id", equalTo("Default")) + .body("assigned-time-series.size()", equalTo(0)); + + // Attempt a patch on TS Group of assigned TS owned by SPK + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, officeId) + .body(newDistrictGroupJson) + .when() + .redirects().follow(true) + .redirects().max(3) + .patch("/timeseries/group/" + districtGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)); + + // Retrieve the group and assert the changes + given() + .log().ifValidationFails(LogDetail.ALL, true) + .accept(Formats.JSONV1) + .contentType(Formats.JSONV1) + .header("Authorization", user.toHeaderValue()) + .queryParam(OFFICE, CWMS_OFFICE) + .when() + .redirects().follow(true) + .redirects().max(3) + .get("/timeseries/group/" + newDistrictGroup.getId()) + .then() + .log().ifValidationFails(LogDetail.ALL, true) + .assertThat() + .statusCode(is(HttpServletResponse.SC_OK)) + .body("id", equalTo("Default")) + .body("assigned-time-series.size()", equalTo(1)) + .body("assigned-time-series[0].timeseries-id", equalTo(tsId)); + } } diff --git a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesRecentControllerIT.java b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesRecentControllerIT.java index 1dfa7571e..4de1262df 100644 --- a/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesRecentControllerIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/api/TimeSeriesRecentControllerIT.java @@ -101,7 +101,7 @@ static void cleanup() throws Exception { LOGGER.log(Level.CONFIG, "TimeSeries not found"); } try { - tsGroupDao.unassignAllTs(group); + tsGroupDao.unassignAllTs(group, OFFICE_ID); tsGroupDao.delete(CATEGORY_ID, GROUP_ID, OFFICE_ID); } catch (NotFoundException e) { LOGGER.log(Level.CONFIG, "Group not found"); diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dao/location/kind/OutletDaoRatingIT.java b/cwms-data-api/src/test/java/cwms/cda/data/dao/location/kind/OutletDaoRatingIT.java index a390b0009..56d3c36dc 100644 --- a/cwms-data-api/src/test/java/cwms/cda/data/dao/location/kind/OutletDaoRatingIT.java +++ b/cwms-data-api/src/test/java/cwms/cda/data/dao/location/kind/OutletDaoRatingIT.java @@ -85,8 +85,8 @@ void test_uncontrolled_outlet() throws SQLException { group.getId(), group.getDescription(), SPILLWAY_CURVE_ID, group.getSharedRefLocationId(), group.getLocGroupAttribute()); LocationGroup newGroup = new LocationGroup(modifiedGroup, group.getAssignedLocations()); - locationGroupDao.unassignAllLocs(group); - locationGroupDao.assignLocs(newGroup); + locationGroupDao.unassignAllLocs(group, PROJECT_1_ID.getOfficeId()); + locationGroupDao.assignLocs(newGroup, PROJECT_1_ID.getOfficeId()); Outlet retrievedOutlet = outletDao.retrieveOutlet(PROJECT_SPILLWAY_LOC.getOfficeId(), PROJECT_SPILLWAY_LOC.getName()); assertNotNull(retrievedOutlet); @@ -114,8 +114,8 @@ void test_controlled_outlet() throws SQLException { group.getId(), group.getDescription(), LOW_FLOW_CURVE_ID, group.getSharedRefLocationId(), group.getLocGroupAttribute()); LocationGroup newGroup = new LocationGroup(modifiedGroup, group.getAssignedLocations()); - locationGroupDao.unassignAllLocs(group); - locationGroupDao.assignLocs(newGroup); + locationGroupDao.unassignAllLocs(group, PROJECT_1_ID.getOfficeId()); + locationGroupDao.assignLocs(newGroup, PROJECT_1_ID.getOfficeId()); Outlet retrievedOutlet = outletDao.retrieveOutlet(PROJECT_LOW_FLOW_LOC.getOfficeId(), PROJECT_LOW_FLOW_LOC.getName()); assertNotNull(retrievedOutlet); diff --git a/cwms-data-api/src/test/resources/cwms/cda/api/timeseries_create_SPK.json b/cwms-data-api/src/test/resources/cwms/cda/api/timeseries_create_SPK.json new file mode 100644 index 000000000..ac0717fe2 --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/api/timeseries_create_SPK.json @@ -0,0 +1,50 @@ +{ + "begin": "2021-06-21T08:00:00-0700[PST8PDT]", + "end": "2021-06-21T09:00:00-0700[PST8PDT]", + "interval": "PT15M", + "name": "ZACK.Stage.Inst.5Minutes.0.ZSTORE_TS_TEST", + "office-id": "SPK", + "units": "m", + "page-size": 1000, + "total": 4, + "page": null, + "value-columns": [ + { + "name": "date-time", + "ordinal": 1, + "datatype": "java.sql.Timestamp" + }, + { + "name": "value", + "ordinal": 2, + "datatype": "java.lang.Double" + }, + { + "name": "quality-code", + "ordinal": 3, + "datatype": "int" + } + ], + "values": [ + [ + 1624287600000, + 0.0, + 0 + ], + [ + 1624288500000, + 1.0, + 0 + ], + [ + 1624289400000, + 2.0, + 0 + ], + [ + 1624290300000, + 3.0, + 0 + ] + ] +} \ No newline at end of file diff --git a/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_mixed_ts_group.sql b/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_mixed_ts_group.sql new file mode 100644 index 000000000..b6e3959f0 --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_mixed_ts_group.sql @@ -0,0 +1,35 @@ +begin + + -- unassign a ts to the group + cwms_ts.unassign_ts_group( + p_ts_category_id=>'Test Category2', + p_ts_group_id=>'Test Group2', + p_ts_id=>'Clear Creek.Precip-Cumulative.Inst.15Minutes.0.raw-cda', + p_unassign_all=>'T', + p_db_office_id=>'LRL'); + + + -- unassign a ts to the group + cwms_ts.unassign_ts_group( + p_ts_category_id=>'Test Category2', + p_ts_group_id=>'Test Group2', + p_ts_id=>'Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda', + p_unassign_all=>'T', + p_db_office_id=>'SPK'); + + -- delete a group at CWMS in the mew category + cwms_ts.delete_ts_group('Test Category2', + 'Test Group2', + 'CWMS'); + + -- delete a category at CWMS + cwms_ts.delete_ts_category('Test Category2', + 'T', + 'CWMS'); + + + + + + +end; \ No newline at end of file diff --git a/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_spk_aliases_and_groups.sql b/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_spk_aliases_and_groups.sql new file mode 100644 index 000000000..ffbd20c4d --- /dev/null +++ b/cwms-data-api/src/test/resources/cwms/cda/data/sql/delete_spk_aliases_and_groups.sql @@ -0,0 +1,10 @@ +begin + -- delete a timeseries alias + cwms_ts.unassign_ts_group(p_ts_category_id=>'Test Category', + p_ts_group_id=>'Test Group', + p_ts_id=>'Alder Springs.Precip-Cumulative.Inst.15Minutes.0.raw-cda', + p_unassign_all=>'T', + p_db_office_id=>'SPK'); + cwms_ts.delete_ts_group('Test Category','Test Group','SPK'); + cwms_ts.delete_ts_category('Test Category', 'T', 'SPK'); +end; \ No newline at end of file