From 2f16cb0f48eca4453e3e894b3d45a3aa09e6dcc0 Mon Sep 17 00:00:00 2001 From: mjkwiatkowski Date: Mon, 16 Feb 2026 15:18:21 +0100 Subject: feat: opendc -> kafka -> postgresql works; added protobuf encoding --- .../main/java/org/opendc/web/server/model/Job.java | 166 ---------- .../org/opendc/web/server/model/Portfolio.java | 151 --------- .../java/org/opendc/web/server/model/Project.java | 237 -------------- .../web/server/model/ProjectAuthorization.java | 172 ---------- .../java/org/opendc/web/server/model/Scenario.java | 197 ------------ .../java/org/opendc/web/server/model/Topology.java | 153 --------- .../java/org/opendc/web/server/model/Trace.java | 72 ----- .../opendc/web/server/model/UserAccounting.java | 167 ---------- .../java/org/opendc/web/server/model/Workload.java | 61 ---- .../org/opendc/web/server/rest/BaseProtocol.java | 50 --- .../opendc/web/server/rest/SchedulerResource.java | 52 --- .../org/opendc/web/server/rest/TraceResource.java | 70 ---- .../MissingKotlinParameterExceptionMapper.java | 46 --- .../rest/error/WebApplicationExceptionMapper.java | 51 --- .../opendc/web/server/rest/runner/JobResource.java | 110 ------- .../web/server/rest/runner/RunnerProtocol.java | 78 ----- .../web/server/rest/user/PortfolioResource.java | 161 --------- .../rest/user/PortfolioScenarioResource.java | 159 --------- .../web/server/rest/user/ProjectResource.java | 131 -------- .../web/server/rest/user/ScenarioResource.java | 127 -------- .../web/server/rest/user/TopologyResource.java | 208 ------------ .../opendc/web/server/rest/user/UserProtocol.java | 132 -------- .../opendc/web/server/rest/user/UserResource.java | 73 ----- .../org/opendc/web/server/service/JobService.java | 81 ----- .../web/server/service/UserAccountingService.java | 136 -------- .../web/server/util/DevSecurityOverrideFilter.java | 64 ---- .../web/server/util/KotlinModuleCustomizer.java | 39 --- .../server/util/QuarkusObjectMapperSupplier.java | 39 --- .../web/server/util/runner/QuarkusJobManager.java | 114 ------- .../src/main/resources/META-INF/branding/logo.png | Bin 2825 -> 0 bytes .../src/main/resources/application-dev.properties | 36 --- .../main/resources/application-docker.properties | 49 --- .../src/main/resources/application-prod.properties | 36 --- .../src/main/resources/application-test.properties | 43 --- .../src/main/resources/application.properties | 49 --- .../main/resources/hypersistence-utils.properties | 1 - .../src/main/resources/load_data.sql | 124 ------- .../web/server/rest/SchedulerResourceTest.java | 44 --- .../opendc/web/server/rest/TraceResourceTest.java | 66 ---- .../web/server/rest/runner/JobResourceTest.java | 144 --------- .../server/rest/user/PortfolioResourceTest.java | 284 ---------------- .../rest/user/PortfolioScenarioResourceTest.java | 273 ---------------- .../web/server/rest/user/ProjectResourceTest.java | 196 ----------- .../web/server/rest/user/ScenarioResourceTest.java | 178 ---------- .../web/server/rest/user/TopologyResourceTest.java | 358 --------------------- .../web/server/rest/user/UserResourceTest.java | 65 ---- .../opendc/web/server/service/JobServiceTest.java | 124 ------- .../server/service/UserAccountingServiceTest.java | 213 ------------ 48 files changed, 5580 deletions(-) delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/BaseProtocol.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/RunnerProtocol.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserProtocol.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java delete mode 100644 opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java delete mode 100644 opendc-web/opendc-web-server/src/main/resources/META-INF/branding/logo.png delete mode 100644 opendc-web/opendc-web-server/src/main/resources/application-dev.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/application-docker.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/application-prod.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/application-test.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/application.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/hypersistence-utils.properties delete mode 100644 opendc-web/opendc-web-server/src/main/resources/load_data.sql delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java delete mode 100644 opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java (limited to 'opendc-web/opendc-web-server/src') diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java deleted file mode 100644 index a0ac390f..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Job.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.hypersistence.utils.hibernate.type.json.JsonType; -import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.*; -import java.time.Instant; -import java.util.Map; -import org.hibernate.annotations.Type; -import org.opendc.web.proto.JobState; - -/** - * A simulation job to be run by the simulator. - */ -@Entity -@Table -@NamedQueries({ - @NamedQuery( - name = "Job.updateOne", - query = - """ - UPDATE Job j - SET j.state = :newState, j.updatedAt = :updatedAt, j.runtime = :runtime, j.results = :results - WHERE j.id = :id AND j.state = :oldState - """) -}) -public class Job extends PanacheEntityBase { - /** - * The main ID of a project. - * The value starts at 6 to account for the other 5 projects already made by the loading script. - */ - @Id - @SequenceGenerator(name = "jobSeq", sequenceName = "job_id_seq", allocationSize = 1, initialValue = 3) - @GeneratedValue(generator = "jobSeq") - public Long id; - - @ManyToOne(optional = false, fetch = FetchType.EAGER) - @JoinColumn(name = "scenario_id", foreignKey = @ForeignKey(name = "fk_jobs_scenario"), nullable = false) - public Scenario scenario; - - @Column(name = "created_by", nullable = false, updatable = false) - public String createdBy; - - @Column(name = "created_at", nullable = false, updatable = false) - public Instant createdAt; - - /** - * The number of simulation runs to perform. - */ - @Column(nullable = false, updatable = false) - public int repeats; - - /** - * The instant at which the job was updated. - */ - @Column(name = "updated_at", nullable = false) - public Instant updatedAt; - - /** - * The state of the job. - */ - @Enumerated(EnumType.STRING) - @Column(nullable = false) - public JobState state = JobState.PENDING; - - /** - * The runtime of the job (in seconds). - */ - @Column(nullable = false) - public int runtime = 0; - - /** - * Experiment results in JSON - */ - @Column(columnDefinition = "jsonb") - @Type(JsonType.class) - public Map results = null; - - /** - * Construct a {@link Job} instance. - */ - public Job(Scenario scenario, String createdBy, Instant createdAt, int repeats) { - this.createdBy = createdBy; - this.scenario = scenario; - this.createdAt = createdAt; - this.updatedAt = createdAt; - this.repeats = repeats; - } - - /** - * JPA constructor - */ - protected Job() {} - - /** - * Find {@link Job}s in the specified {@link JobState}. - * - * @param state The state of the jobs to find. - * @return A query for jobs that are in the specified state. - */ - public static PanacheQuery findByState(JobState state) { - return find("state", state); - } - - /** - * Atomically update this job. - * - * @param newState The new state to enter into. - * @param time The time at which the update occurs. - * @param results The results to possible set. - * @return true when the update succeeded`, false when there was a conflict. - */ - public boolean updateAtomically(JobState newState, Instant time, int runtime, Map results) { - long count = update( - "#Job.updateOne", - Parameters.with("id", id) - .and("oldState", state) - .and("newState", newState) - .and("updatedAt", time) - .and("runtime", runtime) - .and("results", results)); - Panache.getEntityManager().refresh(this); - return count > 0; - } - - /** - * Determine whether the job is allowed to transition to newState. - * - * @param newState The new state to transition to. - * @return true if the transition to the new state is legal, false otherwise. - */ - public boolean canTransitionTo(JobState newState) { - // Note that we always allow transitions from the state - return newState == this.state - || switch (this.state) { - case PENDING -> newState == JobState.CLAIMED; - case CLAIMED -> newState == JobState.RUNNING || newState == JobState.FAILED; - case RUNNING -> newState == JobState.FINISHED || newState == JobState.FAILED; - case FINISHED, FAILED -> false; - }; - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java deleted file mode 100644 index c2695192..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Portfolio.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.hypersistence.utils.hibernate.type.json.JsonType; -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OrderBy; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import java.util.HashSet; -import java.util.Set; -import org.hibernate.annotations.Type; -import org.opendc.web.proto.Targets; - -/** - * A portfolio is the composition of multiple scenarios. - */ -@Entity -@Table( - uniqueConstraints = { - @UniqueConstraint( - name = "uk_portfolios_number", - columnNames = {"project_id", "number"}) - }, - indexes = {@Index(name = "ux_portfolios_number", columnList = "project_id, number")}) -@NamedQueries({ - @NamedQuery(name = "Portfolio.findByProject", query = "SELECT p FROM Portfolio p WHERE p.project.id = :projectId"), - @NamedQuery( - name = "Portfolio.findOneByProject", - query = "SELECT p FROM Portfolio p WHERE p.project.id = :projectId AND p.number = :number") -}) -public class Portfolio extends PanacheEntityBase { - - /** - * The main ID of a project. - * The value starts at 6 to account for the other 5 projects already made by the loading script. - */ - @Id - @SequenceGenerator(name = "portfolioSeq", sequenceName = "portfolio_id_seq", allocationSize = 1, initialValue = 4) - @GeneratedValue(generator = "portfolioSeq") - public Long id; - - /** - * The {@link Project} this portfolio belongs to. - */ - @ManyToOne(optional = false) - @JoinColumn(name = "project_id", nullable = false) - public Project project; - - /** - * Unique number of the portfolio for the project. - */ - @Column(nullable = false) - public int number; - - /** - * The name of this portfolio. - */ - @Column(nullable = false) - public String name; - - /** - * The portfolio targets (metrics, repetitions). - */ - @Column(columnDefinition = "jsonb", nullable = false, updatable = false) - @Type(JsonType.class) - public Targets targets; - - /** - * The scenarios in this portfolio. - */ - @OneToMany( - cascade = {CascadeType.ALL}, - mappedBy = "portfolio", - orphanRemoval = true) - @OrderBy("id ASC") - public Set scenarios = new HashSet<>(); - - /** - * Construct a {@link Portfolio} object. - */ - public Portfolio(Project project, int number, String name, Targets targets) { - this.project = project; - this.number = number; - this.name = name; - this.targets = targets; - } - - /** - * JPA constructor - */ - protected Portfolio() {} - - /** - * Find all {@link Portfolio}s that belong to the specified project - * - * @param projectId The unique identifier of the project. - * @return The query of portfolios that belong to the specified project. - */ - public static PanacheQuery findByProject(long projectId) { - return find("#Portfolio.findByProject", Parameters.with("projectId", projectId)); - } - - /** - * Find the {@link Portfolio} with the specified number belonging to the specified project. - * - * @param projectId The unique identifier of the project. - * @param number The number of the scenario. - * @return The portfolio or null if it does not exist. - */ - public static Portfolio findByProject(long projectId, int number) { - return find( - "#Portfolio.findOneByProject", - Parameters.with("projectId", projectId).and("number", number)) - .firstResult(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java deleted file mode 100644 index f4e5305d..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Project.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.OneToMany; -import jakarta.persistence.OrderBy; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; -import java.time.Instant; -import java.util.HashSet; -import java.util.Set; - -/** - * A project in OpenDC encapsulates all the datacenter designs and simulation runs for a set of users. - */ -@Entity -@Table -@NamedQueries({ - @NamedQuery( - name = "Project.findByUser", - query = - """ - SELECT a - FROM ProjectAuthorization a - WHERE a.key.userName = :userName - """), - @NamedQuery( - name = "Project.allocatePortfolio", - query = - """ - UPDATE Project p - SET p.portfoliosCreated = :oldState + 1, p.updatedAt = :now - WHERE p.id = :id AND p.portfoliosCreated = :oldState - """), - @NamedQuery( - name = "Project.allocateTopology", - query = - """ - UPDATE Project p - SET p.topologiesCreated = :oldState + 1, p.updatedAt = :now - WHERE p.id = :id AND p.topologiesCreated = :oldState - """), - @NamedQuery( - name = "Project.allocateScenario", - query = - """ - UPDATE Project p - SET p.scenariosCreated = :oldState + 1, p.updatedAt = :now - WHERE p.id = :id AND p.scenariosCreated = :oldState - """) -}) -public class Project extends PanacheEntityBase { - - /** - * The main ID of a project. - * The value starts at 6 to account for the other 5 projects already made by the loading script. - */ - @Id - @SequenceGenerator(name = "projectSeq", sequenceName = "project_id_seq", allocationSize = 1, initialValue = 7) - @GeneratedValue(generator = "projectSeq") - public Long id; - - /** - * The name of the project. - */ - @Column(nullable = false) - public String name; - - /** - * The instant at which the project was created. - */ - @Column(name = "created_at", nullable = false, updatable = false) - public Instant createdAt; - - /** - * The instant at which the project was updated. - */ - @Column(name = "updated_at", nullable = false) - public Instant updatedAt; - - /** - * The portfolios belonging to this project. - */ - @OneToMany( - cascade = {CascadeType.ALL}, - mappedBy = "project", - orphanRemoval = true) - @OrderBy("id ASC") - public Set portfolios = new HashSet<>(); - - /** - * The number of portfolios created for this project (including deleted portfolios). - */ - @Column(name = "portfolios_created", nullable = false) - public int portfoliosCreated = 0; - - /** - * The topologies belonging to this project. - */ - @OneToMany( - cascade = {CascadeType.ALL}, - mappedBy = "project", - orphanRemoval = true) - @OrderBy("id ASC") - public Set topologies = new HashSet<>(); - - /** - * The number of topologies created for this project (including deleted topologies). - */ - @Column(name = "topologies_created", nullable = false) - public int topologiesCreated = 0; - - /** - * The scenarios belonging to this project. - */ - @OneToMany(mappedBy = "project", orphanRemoval = true) - public Set scenarios = new HashSet<>(); - - /** - * The number of scenarios created for this project (including deleted scenarios). - */ - @Column(name = "scenarios_created", nullable = false) - public int scenariosCreated = 0; - - /** - * The users authorized to access the project. - */ - @OneToMany( - cascade = {CascadeType.ALL}, - mappedBy = "project", - orphanRemoval = true) - public Set authorizations = new HashSet<>(); - - /** - * Construct a {@link Project} object. - */ - public Project(String name, Instant createdAt) { - this.name = name; - this.createdAt = createdAt; - this.updatedAt = createdAt; - } - - /** - * JPA constructor - */ - protected Project() {} - - /** - * Allocate the next portfolio number for the specified [project]. - * - * @param time The time at which the new portfolio is created. - */ - public int allocatePortfolio(Instant time) { - for (int i = 0; i < 4; i++) { - long count = update( - "#Project.allocatePortfolio", - Parameters.with("id", id).and("oldState", portfoliosCreated).and("now", time)); - if (count > 0) { - return portfoliosCreated + 1; - } else { - Panache.getEntityManager().refresh(this); - } - } - - throw new IllegalStateException("Failed to allocate next portfolio"); - } - - /** - * Allocate the next topology number for the specified [project]. - * - * @param time The time at which the new topology is created. - */ - public int allocateTopology(Instant time) { - for (int i = 0; i < 4; i++) { - long count = update( - "#Project.allocateTopology", - Parameters.with("id", id).and("oldState", topologiesCreated).and("now", time)); - if (count > 0) { - return topologiesCreated + 1; - } else { - Panache.getEntityManager().refresh(this); - } - } - - throw new IllegalStateException("Failed to allocate next topology"); - } - - /** - * Allocate the next scenario number for the specified [project]. - * - * @param time The time at which the new scenario is created. - */ - public int allocateScenario(Instant time) { - for (int i = 0; i < 4; i++) { - long count = update( - "#Project.allocateScenario", - Parameters.with("id", id).and("oldState", scenariosCreated).and("now", time)); - if (count > 0) { - return scenariosCreated + 1; - } else { - Panache.getEntityManager().refresh(this); - } - } - - throw new IllegalStateException("Failed to allocate next scenario"); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java deleted file mode 100644 index 3776ae12..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/ProjectAuthorization.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.EmbeddedId; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.MapsId; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.Table; -import java.io.Serializable; -import java.util.Objects; -import org.opendc.web.proto.user.ProjectRole; - -/** - * An authorization for some user to participate in a project. - */ -@Entity -@Table -@NamedQueries({ - @NamedQuery( - name = "ProjectAuthorization.findByUser", - query = - """ - SELECT a - FROM ProjectAuthorization a - WHERE a.key.userName = :userName - """), -}) -public class ProjectAuthorization extends PanacheEntityBase { - /** - * The user identifier of the authorization. - */ - @EmbeddedId - public ProjectAuthorization.Key key; - - /** - * The project that the user is authorized to participate in. - */ - @ManyToOne(optional = false, fetch = FetchType.LAZY) - @MapsId("projectId") - @JoinColumn( - name = "project_id", - updatable = false, - insertable = false, - nullable = false, - foreignKey = @ForeignKey(name = "fk_project_authorizations")) - public Project project; - - /** - * The role of the user in the project. - */ - @Column(nullable = false) - @Enumerated(EnumType.STRING) - public ProjectRole role; - - /** - * Construct a {@link ProjectAuthorization} object. - */ - public ProjectAuthorization(Project project, String userName, ProjectRole role) { - this.key = new ProjectAuthorization.Key(project.id, userName); - this.project = project; - this.role = role; - } - - /** - * JPA constructor - */ - protected ProjectAuthorization() {} - - /** - * List all projects for the user with the specified userName. - * - * @param userName The identifier of the user that is requesting the list of projects. - * @return A query returning projects that the user has received authorization for. - */ - public static PanacheQuery findByUser(String userName) { - return find("#ProjectAuthorization.findByUser", Parameters.with("userName", userName)); - } - - /** - * Find the project with id for the user with the specified userName. - * - * @param userName The identifier of the user that is requesting the list of projects. - * @param project_id The unique identifier of the project. - * @return The project with the specified identifier or null if it does not exist or is not accessible - * to the user with the specified identifier. - */ - public static ProjectAuthorization findByUser(String userName, long project_id) { - return findById(new ProjectAuthorization.Key(project_id, userName)); - } - - /** - * Determine whether the authorization allows the user to edit the project. - */ - public boolean canEdit() { - return switch (role) { - case OWNER, EDITOR -> true; - case VIEWER -> false; - }; - } - - /** - * Determine whether the authorization allows the user to delete the project. - */ - public boolean canDelete() { - return role == ProjectRole.OWNER; - } - - /** - * Key for representing a {@link ProjectAuthorization} object. - */ - @Embeddable - public static class Key implements Serializable { - @Column(name = "project_id", nullable = false) - public long projectId; - - @Column(name = "user_name", nullable = false) - public String userName; - - public Key(long projectId, String userName) { - this.projectId = projectId; - this.userName = userName; - } - - protected Key() {} - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Key key = (Key) o; - return projectId == key.projectId && userName.equals(key.userName); - } - - @Override - public int hashCode() { - return Objects.hash(projectId, userName); - } - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java deleted file mode 100644 index c79ef5bb..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Scenario.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.hypersistence.utils.hibernate.type.json.JsonType; -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.*; -import java.util.ArrayList; -import java.util.List; -import org.hibernate.annotations.Type; -import org.opendc.web.proto.OperationalPhenomena; - -/** - * A single scenario to be explored by the simulator. - */ -@Entity -@Table( - uniqueConstraints = { - @UniqueConstraint( - name = "uk_scenarios_number", - columnNames = {"project_id", "number"}) - }, - indexes = {@Index(name = "ux_scenarios_number", columnList = "project_id, number")}) -@NamedQueries({ - @NamedQuery(name = "Scenario.findByProject", query = "SELECT s FROM Scenario s WHERE s.project.id = :projectId"), - @NamedQuery( - name = "Scenario.findByPortfolio", - query = - """ - SELECT s - FROM Scenario s - JOIN Portfolio p ON p.id = s.portfolio.id AND p.number = :number - WHERE s.project.id = :projectId - """), - @NamedQuery( - name = "Scenario.findOneByProject", - query = "SELECT s FROM Scenario s WHERE s.project.id = :projectId AND s.number = :number") -}) -public class Scenario extends PanacheEntityBase { - /** - * The main ID of a Scenario. - * The value starts at 3 to account for the other 2 scenarios already made by the loading script. - */ - @Id - @SequenceGenerator(name = "scenarioSeq", sequenceName = "scenario_id_seq", allocationSize = 1, initialValue = 3) - @GeneratedValue(generator = "scenarioSeq") - public Long id; - - /** - * The {@link Project} to which this scenario belongs. - */ - @ManyToOne(optional = false) - @JoinColumn(name = "project_id", nullable = false, foreignKey = @ForeignKey(name = "fk_scenarios_project")) - public Project project; - - /** - * The {@link Portfolio} to which this scenario belongs. - */ - @ManyToOne(optional = false) - @JoinColumn(name = "portfolio_id", nullable = false, foreignKey = @ForeignKey(name = "fk_scenarios_portfolio")) - public Portfolio portfolio; - - /** - * Unique number of the scenario for the project. - */ - @Column(nullable = false) - public int number; - - /** - * The name of the scenario. - */ - @Column(nullable = false, updatable = false) - public String name; - - /** - * Workload details of the scenario. - */ - @Embedded - public Workload workload; - - /** - * Topology details of the scenario. - */ - @ManyToOne(optional = false) - @JoinColumn(name = "topology_id", nullable = false, foreignKey = @ForeignKey(name = "fk_scenarios_topology")) - public Topology topology; - - /** - * Operational phenomena activated in the scenario. - * @Column(columnDefinition = "jsonb", nullable = false, updatable = false) - * @Type(JsonType.class) - */ - @Column(columnDefinition = "jsonb", nullable = false, updatable = false) - @Type(JsonType.class) - public OperationalPhenomena phenomena; - - /** - * The name of the VM scheduler used in the scenario. - */ - @Column(name = "scheduler_name", nullable = false, updatable = false) - public String schedulerName; - - /** - * The {@link Job} associated with the scenario. - */ - @OneToMany( - cascade = {CascadeType.ALL}, - mappedBy = "scenario", - fetch = FetchType.LAZY) - public List jobs = new ArrayList<>(); - - /** - * Construct a {@link Scenario} object. - */ - public Scenario( - Project project, - Portfolio portfolio, - int number, - String name, - Workload workload, - Topology topology, - OperationalPhenomena phenomena, - String schedulerName) { - this.project = project; - this.portfolio = portfolio; - this.number = number; - this.name = name; - this.workload = workload; - this.topology = topology; - this.phenomena = phenomena; - this.schedulerName = schedulerName; - } - - /** - * JPA constructor - */ - protected Scenario() {} - - /** - * Find all {@link Scenario}s that belong to the specified project - * - * @param projectId The unique identifier of the project. - * @return The query of scenarios that belong to the specified project. - */ - public static PanacheQuery findByProject(long projectId) { - return find("#Scenario.findByProject", Parameters.with("projectId", projectId)); - } - - /** - * Find all {@link Scenario}s that belong to the specified portfolio. - * - * @param projectId The unique identifier of the project. - * @param number The number of the portfolio. - * @return The query of scenarios that belong to the specified project and portfolio.. - */ - public static PanacheQuery findByPortfolio(long projectId, int number) { - return find( - "#Scenario.findByPortfolio", - Parameters.with("projectId", projectId).and("number", number)); - } - - /** - * Find the {@link Scenario} with the specified number belonging to the specified project. - * - * @param projectId The unique identifier of the project. - * @param number The number of the scenario. - * @return The scenario or null if it does not exist. - */ - public static Scenario findByProject(long projectId, int number) { - return find( - "#Scenario.findOneByProject", - Parameters.with("projectId", projectId).and("number", number)) - .firstResult(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java deleted file mode 100644 index 8a4e2ae2..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Topology.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.hypersistence.utils.hibernate.type.json.JsonType; -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.hibernate.orm.panache.PanacheQuery; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.SequenceGenerator; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import java.time.Instant; -import java.util.List; -import org.hibernate.annotations.Type; -import org.opendc.web.proto.Room; - -/** - * A datacenter design in OpenDC. - */ -@Entity -@Table( - uniqueConstraints = { - @UniqueConstraint( - name = "uk_topologies_number", - columnNames = {"project_id", "number"}) - }, - indexes = {@Index(name = "ux_topologies_number", columnList = "project_id, number")}) -@NamedQueries({ - @NamedQuery(name = "Topology.findByProject", query = "SELECT t FROM Topology t WHERE t.project.id = :projectId"), - @NamedQuery( - name = "Topology.findOneByProject", - query = "SELECT t FROM Topology t WHERE t.project.id = :projectId AND t.number = :number") -}) -public class Topology extends PanacheEntityBase { - /** - * The main ID of a project. - * The value starts at 6 to account for the other 5 projects already made by the loading script. - */ - @Id - @SequenceGenerator(name = "topologySeq", sequenceName = "topology_id_seq", allocationSize = 1, initialValue = 5) - @GeneratedValue(generator = "topologySeq") - public Long id; - - /** - * The {@link Project} to which the topology belongs. - */ - @ManyToOne(optional = false) - @JoinColumn(name = "project_id", nullable = false) - public Project project; - - /** - * Unique number of the topology for the project. - */ - @Column(nullable = false) - public int number; - - /** - * The name of the topology. - */ - @Column(nullable = false) - public String name; - - /** - * The instant at which the topology was created. - */ - @Column(name = "created_at", nullable = false, updatable = false) - public Instant createdAt; - - /** - * The instant at which the topology was updated. - */ - @Column(name = "updated_at", nullable = false) - public Instant updatedAt; - - /** - * Datacenter design in JSON - * @Column(columnDefinition = "jsonb", nullable = false) - * @Type(JsonType.class) - */ - @Column(columnDefinition = "jsonb", nullable = false) - @Type(JsonType.class) - public List rooms; - - /** - * Construct a {@link Topology} object. - */ - public Topology(Project project, int number, String name, Instant createdAt, List rooms) { - this.project = project; - this.number = number; - this.name = name; - this.createdAt = createdAt; - this.updatedAt = createdAt; - this.rooms = rooms; - } - - /** - * JPA constructor - */ - protected Topology() {} - - /** - * Find all [Topology]s that belong to [project][projectId]. - * - * @param projectId The unique identifier of the project. - * @return The query of topologies that belong to the specified project. - */ - public static PanacheQuery findByProject(long projectId) { - return find("#Topology.findByProject", Parameters.with("projectId", projectId)); - } - - /** - * Find the [Topology] with the specified [number] belonging to [project][projectId]. - * - * @param projectId The unique identifier of the project. - * @param number The number of the topology. - * @return The topology or `null` if it does not exist. - */ - public static Topology findByProject(long projectId, int number) { - return find( - "#Topology.findOneByProject", - Parameters.with("projectId", projectId).and("number", number)) - .firstResult(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java deleted file mode 100644 index 71c647bc..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Trace.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; - -/** - * A workload trace available for simulation. - */ -@Entity -@Table -public class Trace extends PanacheEntityBase { - /** - * The unique identifier of the trace. - */ - @Id - public String id; - - /** - * The name of the trace. - */ - @Column(nullable = false, updatable = false) - public String name; - - /** - * The type of trace. - */ - @Column(nullable = false, updatable = false) - public String type; - - /** - * Construct a {@link Trace}. - * - * @param id The unique identifier of the trace. - * @param name The name of the trace. - * @param type The type of trace. - */ - public Trace(String id, String name, String type) { - this.id = id; - this.name = name; - this.type = type; - } - - /** - * JPA constructor. - */ - protected Trace() {} -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java deleted file mode 100644 index 10a10ef9..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/UserAccounting.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import io.quarkus.hibernate.orm.panache.PanacheEntityBase; -import io.quarkus.panache.common.Parameters; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.NamedQueries; -import jakarta.persistence.NamedQuery; -import jakarta.persistence.Table; -import java.time.LocalDate; - -/** - * Entity to track the number of simulation minutes used by a user. - */ -@Entity -@Table -@NamedQueries({ - @NamedQuery( - name = "UserAccounting.consumeBudget", - query = - """ - UPDATE UserAccounting a - SET a.simulationTime = a.simulationTime + :seconds - WHERE a.userId = :userId AND a.periodEnd = :periodEnd - """), - @NamedQuery( - name = "UserAccounting.resetBudget", - query = - """ - UPDATE UserAccounting a - SET a.periodEnd = :periodEnd, a.simulationTime = :seconds - WHERE a.userId = :userId AND a.periodEnd = :oldPeriodEnd - """) -}) -public class UserAccounting extends PanacheEntityBase { - /** - * User to which this object belongs. - */ - @Id - @Column(name = "user_id", nullable = false) - public String userId; - - /** - * The end of the accounting period. - */ - @Column(name = "period_end", nullable = false) - public LocalDate periodEnd; - - /** - * The number of simulation seconds to be used per accounting period. - */ - @Column(name = "simulation_time_budget", nullable = false) - public int simulationTimeBudget; - - /** - * The number of simulation seconds used in this period. This number should reset once the accounting period has - * been reached. - */ - @Column(name = "simulation_time", nullable = false) - public int simulationTime = 0; - - /** - * Construct a new {@link UserAccounting} object. - * - * @param userId The identifier of the user that this object belongs to. - * @param periodEnd The end of the accounting period. - * @param simulationTimeBudget The number of simulation seconds available per accounting period. - */ - public UserAccounting(String userId, LocalDate periodEnd, int simulationTimeBudget) { - this.userId = userId; - this.periodEnd = periodEnd; - this.simulationTimeBudget = simulationTimeBudget; - } - - /** - * JPA constructor. - */ - protected UserAccounting() {} - - /** - * Return the {@link UserAccounting} object associated with the specified user id. - */ - public static UserAccounting findByUser(String userId) { - return findById(userId); - } - - /** - * Create a new {@link UserAccounting} object and persist it to the database. - * - * @param userId The identifier of the user that this object belongs to. - * @param periodEnd The end of the accounting period. - * @param simulationTimeBudget The number of simulation seconds available per accounting period. - * @param simulationTime The initial simulation time that has been consumed. - */ - public static UserAccounting create( - String userId, LocalDate periodEnd, int simulationTimeBudget, int simulationTime) { - UserAccounting newAccounting = new UserAccounting(userId, periodEnd, simulationTimeBudget); - newAccounting.simulationTime = simulationTime; - newAccounting.persistAndFlush(); - return newAccounting; - } - - /** - * Atomically consume the budget for this {@link UserAccounting} object. - * - * @param seconds The number of seconds to consume from the user. - * @return true when the update succeeded, false when there was a conflict. - */ - public boolean consumeBudget(int seconds) { - long count = update( - "#UserAccounting.consumeBudget", - Parameters.with("userId", userId).and("periodEnd", periodEnd).and("seconds", seconds)); - return count > 0; - } - - /** - * Atomically reset the budget for this {@link UserAccounting} object. - * - * @param periodEnd The new end period for the budget. - * @param seconds The number of seconds that have already been consumed. - * @return true when the update succeeded`, false when there was a conflict. - */ - public boolean resetBudget(LocalDate periodEnd, int seconds) { - long count = update( - "#UserAccounting.resetBudget", - Parameters.with("userId", userId) - .and("oldPeriodEnd", this.periodEnd) - .and("periodEnd", periodEnd) - .and("seconds", seconds)); - return count > 0; - } - - /** - * Determine whether the user has any remaining simulation budget. - * - * @return true when the user still has budget left, false otherwise. - */ - public boolean hasSimulationBudget() { - var today = LocalDate.now(); - - // The accounting period must be over or there must be budget remaining. - return !today.isBefore(periodEnd) || simulationTimeBudget > simulationTime; - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java deleted file mode 100644 index fd7010d2..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/model/Workload.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.model; - -import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; -import jakarta.persistence.ManyToOne; - -/** - * Specification of the workload for a {@link Scenario} - */ -@Embeddable -public class Workload { - /** - * The {@link Trace} that the workload runs. - */ - @ManyToOne(optional = false) - public Trace trace; - - /** - * The percentage of the trace that should be sampled. - */ - @Column(name = "sampling_fraction", nullable = false, updatable = false) - public double samplingFraction; - - /** - * Construct a {@link Workload} object. - * - * @param trace The {@link Trace} to run as workload. - * @param samplingFraction The percentage of the workload to sample. - */ - public Workload(Trace trace, double samplingFraction) { - this.trace = trace; - this.samplingFraction = samplingFraction; - } - - /** - * JPA constructor. - */ - protected Workload() {} -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/BaseProtocol.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/BaseProtocol.java deleted file mode 100644 index 44d2d569..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/BaseProtocol.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest; - -import org.opendc.web.server.model.Trace; -import org.opendc.web.server.model.Workload; - -/** - * DTO-conversions for the base protocol. - */ -public final class BaseProtocol { - /** - * Private constructor to prevent instantiation of class. - */ - private BaseProtocol() {} - - /** - * Convert a {@link Workload} entity into a DTO. - */ - public static org.opendc.web.proto.Workload toDto(Workload workload) { - return new org.opendc.web.proto.Workload(toDto(workload.trace), workload.samplingFraction); - } - - /** - * Convert a {@link Trace] entity into a {@link org.opendc.web.proto.Trace} DTO. - */ - public static org.opendc.web.proto.Trace toDto(Trace trace) { - return new org.opendc.web.proto.Trace(trace.id, trace.name, trace.type); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java deleted file mode 100644 index 3e839040..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/SchedulerResource.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import java.util.List; - -/** - * A resource representing the available schedulers that can be used during experiments. - */ -@Produces("application/json") -@Path("/schedulers") -public final class SchedulerResource { - /** - * Obtain all available schedulers. - */ - @GET - public List getAll() { - return List.of( - "mem", - "mem-inv", - "core-mem", - "core-mem-inv", - "active-tasks", - "active-tasks-inv", - "provisioned-cores", - "provisioned-cores-inv", - "random"); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java deleted file mode 100644 index daec01cd..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/TraceResource.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest; - -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.util.List; -import java.util.stream.Stream; -import org.opendc.web.server.model.Trace; - -/** - * A resource representing the workload traces available in the OpenDC instance. - */ -@Produces("application/json") -@Path("/traces") -public final class TraceResource { - /** - * Obtain all available traces. - */ - @GET - public List getAll() { - Stream entities = Trace.streamAll(); - return entities.map(TraceResource::toDto).toList(); - } - - /** - * Obtain trace information by identifier. - */ - @GET - @Path("{id}") - public org.opendc.web.proto.Trace get(@PathParam("id") String id) { - Trace trace = Trace.findById(id); - - if (trace == null) { - throw new WebApplicationException("Trace not found", 404); - } - - return toDto(trace); - } - - /** - * Convert a {@link Trace] entity into a {@link org.opendc.web.proto.Trace} DTO. - */ - public static org.opendc.web.proto.Trace toDto(Trace trace) { - return new org.opendc.web.proto.Trace(trace.id, trace.name, trace.type); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java deleted file mode 100644 index 345acdfe..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/MissingKotlinParameterExceptionMapper.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.error; - -import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.ext.ExceptionMapper; -import jakarta.ws.rs.ext.Provider; -import org.opendc.web.proto.ProtocolError; - -/** - * An [ExceptionMapper] for [MissingKotlinParameterException] thrown by Jackson. - */ -@Provider -public final class MissingKotlinParameterExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(MissingKotlinParameterException exception) { - return Response.status(Response.Status.BAD_REQUEST) - .entity(new ProtocolError( - Response.Status.BAD_REQUEST.getStatusCode(), - "Field " + exception.getParameter().getName() + " is missing from body.")) - .type(MediaType.APPLICATION_JSON) - .build(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java deleted file mode 100644 index e027e559..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/error/WebApplicationExceptionMapper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.error; - -import jakarta.ws.rs.WebApplicationException; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.ext.ExceptionMapper; -import jakarta.ws.rs.ext.Provider; -import org.opendc.web.proto.ProtocolError; - -/** - * Helper class to transform a {@link WebApplicationException} into an JSON error response. - */ -@Provider -public final class WebApplicationExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(WebApplicationException exception) { - int code = exception.getResponse().getStatus(); - - String message = exception.getMessage(); - if (message == null) { - message = "Unknown error"; - } - - return Response.status(code) - .entity(new ProtocolError(code, message)) - .type(MediaType.APPLICATION_JSON) - .build(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java deleted file mode 100644 index 4dde8654..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/JobResource.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.runner; - -import jakarta.annotation.security.RolesAllowed; -import jakarta.transaction.Transactional; -import jakarta.validation.Valid; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.util.List; -import org.opendc.web.proto.JobState; -import org.opendc.web.server.model.Job; -import org.opendc.web.server.service.JobService; - -/** - * A resource representing the available simulation jobs. - */ -@Produces("application/json") -@Path("/jobs") -@RolesAllowed("runner") -public final class JobResource { - /** - * The {@link JobService} for helping manage the job lifecycle. - */ - private final JobService jobService; - - /** - * Construct a {@link JobResource} instance. - * - * @param jobService The {@link JobService} for managing the job lifecycle. - */ - public JobResource(JobService jobService) { - this.jobService = jobService; - } - - /** - * Obtain all pending simulation jobs. - */ - @GET - public List queryPending() { - return Job.findByState(JobState.PENDING).list().stream() - .map(RunnerProtocol::toDto) - .toList(); - } - - /** - * Get a job by identifier. - */ - @GET - @Path("{job}") - public org.opendc.web.proto.runner.Job get(@PathParam("job") long id) { - Job job = Job.findById(id); - - if (job == null) { - throw new WebApplicationException("Job not found", 404); - } - - return RunnerProtocol.toDto(job); - } - - /** - * Atomically update the state of a job. - */ - @POST - @Path("{job}") - @Consumes("application/json") - @Transactional - public org.opendc.web.proto.runner.Job update( - @PathParam("job") long id, @Valid org.opendc.web.proto.runner.Job.Update update) { - Job job = Job.findById(id); - if (job == null) { - throw new WebApplicationException("Job not found", 404); - } - - try { - jobService.updateJob(job, update.getState(), update.getRuntime(), update.getResults()); - } catch (IllegalArgumentException e) { - throw new WebApplicationException(e, 400); - } catch (IllegalStateException e) { - throw new WebApplicationException(e, 409); - } - - return RunnerProtocol.toDto(job); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/RunnerProtocol.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/RunnerProtocol.java deleted file mode 100644 index 6bf65d97..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/runner/RunnerProtocol.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.runner; - -import org.opendc.web.server.model.Job; -import org.opendc.web.server.model.Portfolio; -import org.opendc.web.server.model.Scenario; -import org.opendc.web.server.model.Topology; -import org.opendc.web.server.rest.BaseProtocol; - -/** - * DTO-conversions for the runner protocol. - */ -public final class RunnerProtocol { - /** - * Private constructor to prevent instantiation of class. - */ - private RunnerProtocol() {} - - /** - * Convert a {@link Job} into a runner-facing DTO. - */ - public static org.opendc.web.proto.runner.Job toDto(Job job) { - return new org.opendc.web.proto.runner.Job( - job.id, toDto(job.scenario), job.state, job.createdAt, job.updatedAt, job.runtime, job.results); - } - - /** - * Convert a {@link Scenario} into a runner-facing DTO. - */ - public static org.opendc.web.proto.runner.Scenario toDto(Scenario scenario) { - return new org.opendc.web.proto.runner.Scenario( - scenario.id, - scenario.number, - toDto(scenario.portfolio), - scenario.name, - BaseProtocol.toDto(scenario.workload), - toDto(scenario.topology), - scenario.phenomena, - scenario.schedulerName); - } - - /** - * Convert a {@link Portfolio} into a runner-facing DTO. - */ - public static org.opendc.web.proto.runner.Portfolio toDto(Portfolio portfolio) { - return new org.opendc.web.proto.runner.Portfolio( - portfolio.id, portfolio.number, portfolio.name, portfolio.targets); - } - - /** - * Convert a {@link Topology} into a runner-facing DTO. - */ - public static org.opendc.web.proto.runner.Topology toDto(Topology topology) { - return new org.opendc.web.proto.runner.Topology( - topology.id, topology.number, topology.name, topology.rooms, topology.createdAt, topology.updatedAt); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java deleted file mode 100644 index 2a3a40f4..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioResource.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.transaction.Transactional; -import jakarta.validation.Valid; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.time.Instant; -import java.util.List; -import org.opendc.web.server.model.Portfolio; -import org.opendc.web.server.model.ProjectAuthorization; - -/** - * A resource representing the portfolios of a project. - */ -@Produces("application/json") -@Path("/projects/{project}/portfolios") -@RolesAllowed("openid") -public final class PortfolioResource { - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link PortfolioResource}. - * - * @param identity The {@link SecurityIdentity} of the current user. - */ - public PortfolioResource(SecurityIdentity identity) { - this.identity = identity; - } - - /** - * Get all portfolios that belong to the specified project. - */ - @GET - public List getAll(@PathParam("project") long projectId) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - return List.of(); - } - - return Portfolio.findByProject(projectId).list().stream() - .map((p) -> UserProtocol.toDto(p, auth)) - .toList(); - } - - /** - * Create a portfolio for this project. - */ - @POST - @Transactional - @Consumes("application/json") - public org.opendc.web.proto.user.Portfolio create( - @PathParam("project") long projectId, @Valid org.opendc.web.proto.user.Portfolio.Create request) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - var now = Instant.now(); - var project = auth.project; - int number = project.allocatePortfolio(now); - - Portfolio portfolio = new Portfolio(project, number, request.getName(), request.getTargets()); - - project.portfolios.add(portfolio); - portfolio.persist(); - - return UserProtocol.toDto(portfolio, auth); - } - - /** - * Obtain a portfolio by its identifier. - */ - @GET - @Path("{portfolio}") - public org.opendc.web.proto.user.Portfolio get( - @PathParam("project") long projectId, @PathParam("portfolio") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Portfolio not found", 404); - } - - Portfolio portfolio = Portfolio.findByProject(projectId, number); - - if (portfolio == null) { - throw new WebApplicationException("Portfolio not found", 404); - } - - return UserProtocol.toDto(portfolio, auth); - } - - /** - * Delete a portfolio. - */ - @DELETE - @Path("{portfolio}") - @Transactional - public org.opendc.web.proto.user.Portfolio delete( - @PathParam("project") long projectId, @PathParam("portfolio") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Portfolio not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Portfolio entity = Portfolio.findByProject(projectId, number); - if (entity == null) { - throw new WebApplicationException("Portfolio not found", 404); - } - - entity.delete(); - return UserProtocol.toDto(entity, auth); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java deleted file mode 100644 index 789808c8..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/PortfolioScenarioResource.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.transaction.Transactional; -import jakarta.validation.Valid; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.time.Instant; -import java.util.List; -import org.opendc.web.proto.JobState; -import org.opendc.web.server.model.Job; -import org.opendc.web.server.model.Portfolio; -import org.opendc.web.server.model.ProjectAuthorization; -import org.opendc.web.server.model.Scenario; -import org.opendc.web.server.model.Topology; -import org.opendc.web.server.model.Trace; -import org.opendc.web.server.model.Workload; -import org.opendc.web.server.service.UserAccountingService; - -/** - * A resource representing the scenarios of a portfolio. - */ -@Path("/projects/{project}/portfolios/{portfolio}/scenarios") -@RolesAllowed("openid") -@Produces("application/json") -public final class PortfolioScenarioResource { - /** - * The service for managing the user accounting. - */ - private final UserAccountingService accountingService; - - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link PortfolioScenarioResource}. - * - * @param accountingService The {@link UserAccountingService} instance to use. - * @param identity The {@link SecurityIdentity} of the current user. - */ - public PortfolioScenarioResource(UserAccountingService accountingService, SecurityIdentity identity) { - this.accountingService = accountingService; - this.identity = identity; - } - - /** - * Get all scenarios that belong to the specified portfolio. - */ - @GET - public List get( - @PathParam("project") long projectId, @PathParam("portfolio") int portfolioNumber) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - return List.of(); - } - - return org.opendc.web.server.model.Scenario.findByPortfolio(projectId, portfolioNumber).list().stream() - .map((s) -> UserProtocol.toDto(s, auth)) - .toList(); - } - - /** - * Create a scenario for this portfolio. - */ - @POST - @Transactional - @Consumes("application/json") - public org.opendc.web.proto.user.Scenario create( - @PathParam("project") long projectId, - @PathParam("portfolio") int portfolioNumber, - @Valid org.opendc.web.proto.user.Scenario.Create request) { - // User must have access to project - String userId = identity.getPrincipal().getName(); - ProjectAuthorization auth = ProjectAuthorization.findByUser(userId, projectId); - - if (auth == null) { - throw new WebApplicationException("Portfolio not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Portfolio portfolio = Portfolio.findByProject(projectId, portfolioNumber); - - if (portfolio == null) { - throw new WebApplicationException("Portfolio not found", 404); - } - - Topology topology = Topology.findByProject(projectId, (int) request.getTopology()); - if (topology == null) { - throw new WebApplicationException("Referred topology does not exist", 400); - } - - Trace trace = Trace.findById(request.getWorkload().getTrace()); - if (trace == null) { - throw new WebApplicationException("Referred trace does not exist", 400); - } - - var now = Instant.now(); - var project = auth.project; - int number = project.allocateScenario(now); - - Scenario scenario = new Scenario( - project, - portfolio, - number, - request.getName(), - new Workload(trace, request.getWorkload().getSamplingFraction()), - topology, - request.getPhenomena(), - request.getSchedulerName()); - scenario.persist(); - - Job job = new Job(scenario, userId, now, portfolio.targets.getRepeats()); - job.persist(); - - // Fail the job if there is not enough budget for the simulation - if (!accountingService.hasSimulationBudget(userId)) { - job.state = JobState.FAILED; - } - - scenario.jobs.add(job); - portfolio.scenarios.add(scenario); - - return UserProtocol.toDto(scenario, auth); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java deleted file mode 100644 index ae1c959e..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ProjectResource.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.transaction.Transactional; -import jakarta.validation.Valid; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.time.Instant; -import java.util.List; -import org.opendc.web.proto.user.ProjectRole; -import org.opendc.web.server.model.Project; -import org.opendc.web.server.model.ProjectAuthorization; - -/** - * A resource representing the created projects. - */ -@Produces("application/json") -@Path("/projects") -@RolesAllowed("openid") -public final class ProjectResource { - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link ProjectResource}. - * - * @param identity The {@link SecurityIdentity} of the current user. - */ - public ProjectResource(SecurityIdentity identity) { - this.identity = identity; - } - - /** - * Obtain all the projects of the current user. - */ - @GET - public List getAll() { - return ProjectAuthorization.findByUser(identity.getPrincipal().getName()).list().stream() - .map(UserProtocol::toDto) - .toList(); - } - - /** - * Create a new project for the current user. - */ - @POST - @Transactional - @Consumes("application/json") - public org.opendc.web.proto.user.Project create(@Valid org.opendc.web.proto.user.Project.Create request) { - Instant now = Instant.now(); - Project entity = new Project(request.getName(), now); - entity.persist(); - - ProjectAuthorization authorization = - new ProjectAuthorization(entity, identity.getPrincipal().getName(), ProjectRole.OWNER); - - entity.authorizations.add(authorization); - authorization.persist(); - - return UserProtocol.toDto(authorization); - } - - /** - * Obtain a single project by its identifier. - */ - @GET - @Path("{project}") - public org.opendc.web.proto.user.Project get(@PathParam("project") long project_id) { - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), project_id); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } - - return UserProtocol.toDto(auth); - } - - /** - * Delete a project. - */ - @DELETE - @Path("{project}") - @Transactional - public org.opendc.web.proto.user.Project delete(@PathParam("project") long id) { - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), id); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } else if (!auth.canDelete()) { - throw new WebApplicationException("Not allowed to delete project", 403); - } - - auth.project.updatedAt = Instant.now(); - org.opendc.web.proto.user.Project project = UserProtocol.toDto(auth); - auth.project.delete(); - return project; - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java deleted file mode 100644 index bb3eb89b..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/ScenarioResource.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.transaction.Transactional; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.util.List; -import org.opendc.web.server.model.ProjectAuthorization; -import org.opendc.web.server.model.Scenario; - -/** - * A resource representing the scenarios of a portfolio. - */ -@Produces("application/json") -@Path("/projects/{project}/scenarios") -@RolesAllowed("openid") -public final class ScenarioResource { - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link ScenarioResource}. - * - * @param identity The {@link SecurityIdentity} of the current user. - */ - public ScenarioResource(SecurityIdentity identity) { - this.identity = identity; - } - - /** - * Obtain the scenarios belonging to a project. - */ - @GET - public List getAll(@PathParam("project") long projectId) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } - - return Scenario.findByProject(projectId).list().stream() - .map((s) -> UserProtocol.toDto(s, auth)) - .toList(); - } - - /** - * Obtain a scenario by its identifier. - */ - @GET - @Path("{scenario}") - public org.opendc.web.proto.user.Scenario get( - @PathParam("project") long projectId, @PathParam("scenario") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } - - Scenario scenario = Scenario.findByProject(projectId, number); - - if (scenario == null) { - throw new WebApplicationException("Scenario not found", 404); - } - - return UserProtocol.toDto(scenario, auth); - } - - /** - * Delete a scenario. - */ - @DELETE - @Path("{scenario}") - @Transactional - public org.opendc.web.proto.user.Scenario delete( - @PathParam("project") long projectId, @PathParam("scenario") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Project not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Scenario entity = Scenario.findByProject(projectId, number); - if (entity == null) { - throw new WebApplicationException("Scenario not found", 404); - } - - entity.delete(); - return UserProtocol.toDto(entity, auth); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java deleted file mode 100644 index b8c542d3..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/TopologyResource.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.hibernate.orm.panache.Panache; -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.persistence.PersistenceException; -import jakarta.transaction.Transactional; -import jakarta.validation.Valid; -import jakarta.ws.rs.Consumes; -import jakarta.ws.rs.DELETE; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.POST; -import jakarta.ws.rs.PUT; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; -import jakarta.ws.rs.Produces; -import jakarta.ws.rs.WebApplicationException; -import java.time.Instant; -import java.util.List; -import org.opendc.web.server.model.Project; -import org.opendc.web.server.model.ProjectAuthorization; -import org.opendc.web.server.model.Topology; - -/** - * A resource representing the constructed datacenter topologies. - */ -@Produces("application/json") -@Path("/projects/{project}/topologies") -@RolesAllowed("openid") -public final class TopologyResource { - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link TopologyResource}. - * - * @param identity The {@link SecurityIdentity} of the current user. - */ - public TopologyResource(SecurityIdentity identity) { - this.identity = identity; - } - - /** - * Get all topologies that belong to the specified project. - */ - @GET - public List getAll(@PathParam("project") long projectId) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - return List.of(); - } - - return Topology.findByProject(projectId).list().stream() - .map((t) -> UserProtocol.toDto(t, auth)) - .toList(); - } - - /** - * Create a topology for this project. - */ - @POST - @Consumes("application/json") - @Transactional - public org.opendc.web.proto.user.Topology create( - @PathParam("project") long projectId, @Valid org.opendc.web.proto.user.Topology.Create request) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Topology not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Instant now = Instant.now(); - Project project = auth.project; - int number = project.allocateTopology(now); - - Topology topology = new Topology(project, number, request.getName(), now, request.getRooms()); - - project.topologies.add(topology); - topology.persist(); - - return UserProtocol.toDto(topology, auth); - } - - /** - * Obtain a topology by its number. - */ - @GET - @Path("{topology}") - public org.opendc.web.proto.user.Topology get( - @PathParam("project") long projectId, @PathParam("topology") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Topology not found", 404); - } - - Topology topology = Topology.findByProject(projectId, number); - - if (topology == null) { - throw new WebApplicationException("Topology not found", 404); - } - - return UserProtocol.toDto(topology, auth); - } - - /** - * Update the specified topology by its number. - */ - @PUT - @Path("{topology}") - @Consumes("application/json") - @Transactional - public org.opendc.web.proto.user.Topology update( - @PathParam("project") long projectId, - @PathParam("topology") int number, - @Valid org.opendc.web.proto.user.Topology.Update request) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Topology not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Topology entity = Topology.findByProject(projectId, number); - - if (entity == null) { - throw new WebApplicationException("Topology not found", 404); - } - - entity.updatedAt = Instant.now(); - entity.rooms = request.getRooms(); - - return UserProtocol.toDto(entity, auth); - } - - /** - * Delete the specified topology. - */ - @Path("{topology}") - @DELETE - @Transactional - public org.opendc.web.proto.user.Topology delete( - @PathParam("project") long projectId, @PathParam("topology") int number) { - // User must have access to project - ProjectAuthorization auth = - ProjectAuthorization.findByUser(identity.getPrincipal().getName(), projectId); - - if (auth == null) { - throw new WebApplicationException("Topology not found", 404); - } else if (!auth.canEdit()) { - throw new WebApplicationException("Not permitted to edit project", 403); - } - - Topology entity = Topology.findByProject(projectId, number); - - if (entity == null) { - throw new WebApplicationException("Topology not found", 404); - } - - entity.updatedAt = Instant.now(); - entity.delete(); - - try { - // Flush the results, so we can check whether the constraints are not violated - Panache.flush(); - } catch (PersistenceException e) { - throw new WebApplicationException("Topology is still in use", 403); - } - - return UserProtocol.toDto(entity, auth); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserProtocol.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserProtocol.java deleted file mode 100644 index 8196a9d6..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserProtocol.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import org.opendc.web.server.model.Job; -import org.opendc.web.server.model.Portfolio; -import org.opendc.web.server.model.Project; -import org.opendc.web.server.model.ProjectAuthorization; -import org.opendc.web.server.model.Scenario; -import org.opendc.web.server.model.Topology; -import org.opendc.web.server.rest.BaseProtocol; - -/** - * DTO-conversions for the user protocol. - */ -public final class UserProtocol { - /** - * Private constructor to prevent instantiation of class. - */ - private UserProtocol() {} - - /** - * Convert a {@link ProjectAuthorization} entity into a {@link Project} DTO. - */ - public static org.opendc.web.proto.user.Project toDto(ProjectAuthorization auth) { - Project project = auth.project; - return new org.opendc.web.proto.user.Project( - project.id, project.name, project.createdAt, project.updatedAt, auth.role); - } - - /** - * Convert a {@link Portfolio} entity into a {@link org.opendc.web.proto.user.Portfolio} DTO. - */ - public static org.opendc.web.proto.user.Portfolio toDto(Portfolio portfolio, ProjectAuthorization auth) { - return new org.opendc.web.proto.user.Portfolio( - portfolio.id, - portfolio.number, - toDto(auth), - portfolio.name, - portfolio.targets, - portfolio.scenarios.stream().map(UserProtocol::toSummaryDto).toList()); - } - - /** - * Convert a {@link Portfolio} entity into a {@link org.opendc.web.proto.user.Portfolio.Summary} DTO. - */ - public static org.opendc.web.proto.user.Portfolio.Summary toSummaryDto(Portfolio portfolio) { - return new org.opendc.web.proto.user.Portfolio.Summary( - portfolio.id, portfolio.number, portfolio.name, portfolio.targets); - } - - /** - * Convert a {@link Topology} entity into a {@link org.opendc.web.proto.user.Topology} DTO. - */ - public static org.opendc.web.proto.user.Topology toDto(Topology topology, ProjectAuthorization auth) { - return new org.opendc.web.proto.user.Topology( - topology.id, - topology.number, - toDto(auth), - topology.name, - topology.rooms, - topology.createdAt, - topology.updatedAt); - } - - /** - * Convert a {@link Topology} entity into a {@link org.opendc.web.proto.user.Topology.Summary} DTO. - */ - public static org.opendc.web.proto.user.Topology.Summary toSummaryDto(Topology topology) { - return new org.opendc.web.proto.user.Topology.Summary( - topology.id, topology.number, topology.name, topology.createdAt, topology.updatedAt); - } - - /** - * Convert a {@link Scenario} entity into a {@link org.opendc.web.proto.user.Scenario} DTO. - */ - public static org.opendc.web.proto.user.Scenario toDto(Scenario scenario, ProjectAuthorization auth) { - return new org.opendc.web.proto.user.Scenario( - scenario.id, - scenario.number, - toDto(auth), - toSummaryDto(scenario.portfolio), - scenario.name, - BaseProtocol.toDto(scenario.workload), - toSummaryDto(scenario.topology), - scenario.phenomena, - scenario.schedulerName, - scenario.jobs.stream().map(UserProtocol::toDto).toList()); - } - - /** - * Convert a {@link Scenario} entity into a {@link org.opendc.web.proto.user.Scenario.Summary} DTO. - */ - public static org.opendc.web.proto.user.Scenario.Summary toSummaryDto(Scenario scenario) { - return new org.opendc.web.proto.user.Scenario.Summary( - scenario.id, - scenario.number, - scenario.name, - BaseProtocol.toDto(scenario.workload), - toSummaryDto(scenario.topology), - scenario.phenomena, - scenario.schedulerName, - scenario.jobs.stream().map(UserProtocol::toDto).toList()); - } - - /** - * Convert a {@link Job} entity into a {@link org.opendc.web.proto.user.Job} DTO. - */ - public static org.opendc.web.proto.user.Job toDto(Job job) { - return new org.opendc.web.proto.user.Job(job.id, job.state, job.createdAt, job.updatedAt, job.results); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java deleted file mode 100644 index c8cda2b7..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/rest/user/UserResource.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import io.quarkus.security.identity.SecurityIdentity; -import jakarta.annotation.security.RolesAllowed; -import jakarta.ws.rs.GET; -import jakarta.ws.rs.Path; -import jakarta.ws.rs.Produces; -import org.opendc.web.proto.user.User; -import org.opendc.web.proto.user.UserAccounting; -import org.opendc.web.server.service.UserAccountingService; - -/** - * A resource representing the active user. - */ -@Produces("application/json") -@Path("/users") -@RolesAllowed("openid") -public final class UserResource { - /** - * The service for managing the user accounting. - */ - private final UserAccountingService accountingService; - - /** - * The identity of the current user. - */ - private final SecurityIdentity identity; - - /** - * Construct a {@link UserResource}. - * - * @param accountingService The {@link UserAccountingService} instance to use. - * @param identity The {@link SecurityIdentity} of the current user. - */ - public UserResource(UserAccountingService accountingService, SecurityIdentity identity) { - this.accountingService = accountingService; - this.identity = identity; - } - - /** - * Get the current active user data. - */ - @GET - @Path("me") - public User get() { - String userId = identity.getPrincipal().getName(); - UserAccounting accounting = accountingService.getAccounting(userId); - - return new User(userId, accounting); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java deleted file mode 100644 index 70933520..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/JobService.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.service; - -import jakarta.enterprise.context.ApplicationScoped; -import java.time.Instant; -import java.util.Map; -import org.opendc.web.proto.JobState; -import org.opendc.web.server.model.Job; - -/** - * A service for managing the lifecycle of a job and ensuring that the user does not consume - * too much simulation resources. - */ -@ApplicationScoped -public final class JobService { - /** - * The {@link UserAccountingService} responsible for accounting the simulation time of users. - */ - private final UserAccountingService accountingService; - - /** - * Construct a {@link JobService} instance. - * - * @param accountingService The {@link UserAccountingService} for accounting the simulation time of users. - */ - public JobService(UserAccountingService accountingService) { - this.accountingService = accountingService; - } - - /** - * Update the job state. - * - * @param job The {@link Job} to update. - * @param newState The new state to transition the job to. - * @param runtime The runtime (in seconds) consumed by the simulation jbo so far. - * @param results The results to attach to the job. - * @throws IllegalArgumentException if the state transition is invalid. - * @throws IllegalStateException if someone tries to update the job concurrently. - */ - public void updateJob(Job job, JobState newState, int runtime, Map results) { - JobState state = job.state; - - if (!job.canTransitionTo(newState)) { - throw new IllegalArgumentException("Invalid transition from %s to %s".formatted(state, newState)); - } - - Instant now = Instant.now(); - JobState nextState = newState; - int consumedBudget = Math.min(1, runtime - job.runtime); - - // Check whether the user still has any simulation budget left - if (accountingService.consumeSimulationBudget(job.createdBy, consumedBudget) && nextState == JobState.RUNNING) { - nextState = JobState.FAILED; // User has consumed all their budget; cancel the job - } - - if (!job.updateAtomically(nextState, now, runtime, results)) { - throw new IllegalStateException("Conflicting update"); - } - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java deleted file mode 100644 index 73fa2a3e..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/service/UserAccountingService.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.service; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.persistence.EntityExistsException; -import java.time.Duration; -import java.time.LocalDate; -import java.time.temporal.TemporalAdjusters; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.opendc.web.server.model.UserAccounting; - -/** - * Service to track the simulation budget of users. - */ -@ApplicationScoped -public final class UserAccountingService { - /** - * The default simulation budget for new users. - */ - private final Duration simulationBudget; - - /** - * Construct a {@link UserAccountingService} instance. - * - * @param simulationBudget The default simulation budget for new users. - */ - public UserAccountingService( - @ConfigProperty(name = "opendc.accounting.simulation-budget", defaultValue = "2000m") - Duration simulationBudget) { - this.simulationBudget = simulationBudget; - } - - /** - * Return the {@link org.opendc.web.proto.user.UserAccounting} object for the user with the - * specified userId. If the object does not exist in the database, a default value is constructed. - */ - public org.opendc.web.proto.user.UserAccounting getAccounting(String userId) { - UserAccounting accounting = UserAccounting.findByUser(userId); - if (accounting != null) { - return new org.opendc.web.proto.user.UserAccounting( - accounting.periodEnd, accounting.simulationTime, accounting.simulationTimeBudget); - } - - return new org.opendc.web.proto.user.UserAccounting( - getNextAccountingPeriod(LocalDate.now()), 0, (int) simulationBudget.toSeconds()); - } - - /** - * Determine whether the user with userId has any remaining simulation budget. - * - * @param userId The unique identifier of the user. - * @return true when the user still has budget left, false otherwise. - */ - public boolean hasSimulationBudget(String userId) { - UserAccounting accounting = UserAccounting.findByUser(userId); - if (accounting == null) { - return true; - } - return accounting.hasSimulationBudget(); - } - - /** - * Consume seconds from the simulation budget of the user with userId. - * - * @param userId The unique identifier of the user. - * @param seconds The seconds to consume from the simulation budget. - * @return true if the user has consumed his full budget or false if there is still budget - * remaining. - */ - public boolean consumeSimulationBudget(String userId, int seconds) { - LocalDate today = LocalDate.now(); - LocalDate nextAccountingPeriod = getNextAccountingPeriod(today); - - // We need to be careful to prevent conflicts in case of concurrency - // 1. First, we try to create the accounting object if it does not exist yet. This may fail if another instance - // creates the object concurrently. - // 2. Second, we check if the budget needs to be reset and try this atomically. - // 3. Finally, we atomically consume the budget from the object - // This is repeated three times in case there is a conflict - for (int i = 0; i < 3; i++) { - UserAccounting accounting = UserAccounting.findByUser(userId); - - if (accounting == null) { - try { - UserAccounting newAccounting = UserAccounting.create( - userId, nextAccountingPeriod, (int) simulationBudget.toSeconds(), seconds); - return !newAccounting.hasSimulationBudget(); - } catch (EntityExistsException e) { - // Conflict due to concurrency; retry - } - } else { - boolean success; - - if (!today.isBefore(accounting.periodEnd)) { - success = accounting.resetBudget(nextAccountingPeriod, seconds); - } else { - success = accounting.consumeBudget(seconds); - } - - if (success) { - return !accounting.hasSimulationBudget(); - } - } - } - - throw new IllegalStateException("Failed to allocate consume budget due to conflict"); - } - - /** - * Helper method to find next accounting period. - */ - private static LocalDate getNextAccountingPeriod(LocalDate today) { - return today.with(TemporalAdjusters.firstDayOfNextMonth()); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java deleted file mode 100644 index 103f868d..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/DevSecurityOverrideFilter.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.util; - -import io.quarkus.arc.properties.IfBuildProperty; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.ContainerRequestFilter; -import jakarta.ws.rs.container.PreMatching; -import jakarta.ws.rs.core.SecurityContext; -import jakarta.ws.rs.ext.Provider; -import java.security.Principal; - -/** - * Helper class to disable security for the OpenDC web API when in development mode. - */ -@Provider -@PreMatching -@IfBuildProperty(name = "opendc.security.enabled", stringValue = "false") -public class DevSecurityOverrideFilter implements ContainerRequestFilter { - @Override - public void filter(ContainerRequestContext requestContext) { - requestContext.setSecurityContext(new SecurityContext() { - @Override - public Principal getUserPrincipal() { - return () -> "anon"; - } - - @Override - public boolean isUserInRole(String role) { - return true; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public String getAuthenticationScheme() { - return "basic"; - } - }); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java deleted file mode 100644 index ff3ba1cd..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/KotlinModuleCustomizer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.module.kotlin.KotlinModule; -import io.quarkus.jackson.ObjectMapperCustomizer; -import jakarta.inject.Singleton; - -/** - * Helper class to register the Kotlin Jackson module. - */ -@Singleton -public final class KotlinModuleCustomizer implements ObjectMapperCustomizer { - @Override - public void customize(ObjectMapper objectMapper) { - objectMapper.registerModule(new KotlinModule.Builder().build()); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java deleted file mode 100644 index 60ca77e5..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/QuarkusObjectMapperSupplier.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.util; - -import com.fasterxml.jackson.databind.ObjectMapper; -import io.hypersistence.utils.hibernate.type.util.ObjectMapperSupplier; -import io.quarkus.runtime.annotations.RegisterForReflection; -import jakarta.enterprise.inject.spi.CDI; - -/** - * A supplier for an {@link ObjectMapper} used by the Hypersistence utilities. - */ -@RegisterForReflection -public class QuarkusObjectMapperSupplier implements ObjectMapperSupplier { - @Override - public ObjectMapper get() { - return CDI.current().select(ObjectMapper.class).get(); - } -} diff --git a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java b/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java deleted file mode 100644 index 47d397f3..00000000 --- a/opendc-web/opendc-web-server/src/main/java/org/opendc/web/server/util/runner/QuarkusJobManager.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2022 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.util.runner; - -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.transaction.Transactional; -import java.util.Map; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.opendc.web.proto.JobState; -import org.opendc.web.runner.JobManager; -import org.opendc.web.server.model.Job; -import org.opendc.web.server.rest.runner.RunnerProtocol; -import org.opendc.web.server.service.JobService; - -/** - * Implementation of {@link JobManager} that interfaces directly with the database without overhead of the REST API. - */ -@ApplicationScoped -public class QuarkusJobManager implements JobManager { - /** - * The {@link JobService} used to manage the job's lifecycle. - */ - private final JobService jobService; - - /** - * Construct a {@link QuarkusJobManager}. - * - * @param jobService The {@link JobService} for managing the job's lifecycle. - */ - public QuarkusJobManager(JobService jobService) { - this.jobService = jobService; - } - - @Transactional - @Nullable - @Override - public org.opendc.web.proto.runner.Job findNext() { - var job = Job.findByState(JobState.PENDING).firstResult(); - if (job == null) { - return null; - } - - return RunnerProtocol.toDto(job); - } - - @Transactional - @Override - public boolean claim(long id) { - return updateState(id, JobState.CLAIMED, 0, null); - } - - @Transactional - @Override - public boolean heartbeat(long id, int runtime) { - return updateState(id, JobState.RUNNING, runtime, null); - } - - @Transactional - @Override - public void fail(long id, int runtime) { - updateState(id, JobState.FAILED, runtime, null); - } - - @Transactional - @Override - public void finish(long id, int runtime, @NotNull Map results) { - updateState(id, JobState.FINISHED, runtime, results); - } - - /** - * Helper method to update the state of a job. - * - * @param id The unique id of the job. - * @param newState The new state to transition to. - * @param runtime The runtime of the job. - * @param results The results of the job. - * @return true if the operation succeeded, false otherwise. - */ - private boolean updateState(long id, JobState newState, int runtime, Map results) { - Job job = Job.findById(id); - - if (job == null) { - return false; - } - - try { - jobService.updateJob(job, newState, runtime, results); - return true; - } catch (IllegalArgumentException | IllegalStateException e) { - return false; - } - } -} diff --git a/opendc-web/opendc-web-server/src/main/resources/META-INF/branding/logo.png b/opendc-web/opendc-web-server/src/main/resources/META-INF/branding/logo.png deleted file mode 100644 index d743038b..00000000 Binary files a/opendc-web/opendc-web-server/src/main/resources/META-INF/branding/logo.png and /dev/null differ diff --git a/opendc-web/opendc-web-server/src/main/resources/application-dev.properties b/opendc-web/opendc-web-server/src/main/resources/application-dev.properties deleted file mode 100644 index 5fbc4c04..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/application-dev.properties +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2022 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Datasource (H2) -quarkus.datasource.db-kind=h2 -quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;INIT=CREATE TYPE IF NOT EXISTS "JSONB" AS json; - -# Hibernate -quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect -quarkus.flyway.clean-at-start=true - -# Disable authentication -opendc.security.enabled=false - -# Mount web UI at root and API at "/api" -quarkus.resteasy.path=/api - -# Swagger UI -quarkus.smallrye-openapi.servers=http://localhost:8080 diff --git a/opendc-web/opendc-web-server/src/main/resources/application-docker.properties b/opendc-web/opendc-web-server/src/main/resources/application-docker.properties deleted file mode 100644 index eae9ee1e..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/application-docker.properties +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2022 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Configuration for standalone Docker server distribution without web UI. - -# Datasource -quarkus.datasource.db-kind=postgresql -quarkus.datasource.username=${OPENDC_DB_USERNAME} -quarkus.datasource.password=${OPENDC_DB_PASSWORD} -quarkus.datasource.jdbc.url=${OPENDC_DB_URL} - -# Hibernate -quarkus.hibernate-orm.dialect=org.hibernate.dialect.PostgreSQL95Dialect - -# Disable OpenDC web UI -quarkus.opendc-ui.include=false - -# Security -opendc.security.enabled=true -quarkus.oidc.auth-server-url=https://${OPENDC_AUTH0_DOMAIN} -quarkus.oidc.client-id=${OPENDC_AUTH0_AUDIENCE} -quarkus.oidc.token.audience=${quarkus.oidc.client-id} -quarkus.oidc.roles.role-claim-path=scope - -# Swagger UI -quarkus.swagger-ui.oauth-client-id=${OPENDC_AUTH0_DOCS_CLIENT_ID:} -quarkus.swagger-ui.oauth-additional-query-string-params={"audience":"${OPENDC_AUTH0_AUDIENCE:https://api.opendc.org/v2/}"} - -quarkus.smallrye-openapi.security-scheme=oidc -quarkus.smallrye-openapi.security-scheme-name=Auth0 -quarkus.smallrye-openapi.oidc-open-id-connect-url=https://${OPENDC_AUTH0_DOMAIN:opendc.eu.auth0.com}/.well-known/openid-configuration -quarkus.smallrye-openapi.servers=https://api.opendc.org diff --git a/opendc-web/opendc-web-server/src/main/resources/application-prod.properties b/opendc-web/opendc-web-server/src/main/resources/application-prod.properties deleted file mode 100644 index fe997fc0..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/application-prod.properties +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2022 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Datasource (H2) -quarkus.datasource.db-kind=h2 -quarkus.datasource.jdbc.url=jdbc:h2:file:./data/opendc;DB_CLOSE_DELAY=-1;INIT=CREATE TYPE IF NOT EXISTS "JSONB" AS json; - -# Hibernate -quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect - -# Disable authentication -opendc.security.enabled=false -quarkus.oidc.enabled=${opendc.security.enabled} - -# Mount web UI at root and API at "/api" -quarkus.resteasy.path=/api - -# Swagger UI -quarkus.smallrye-openapi.servers=http://localhost:8080 diff --git a/opendc-web/opendc-web-server/src/main/resources/application-test.properties b/opendc-web/opendc-web-server/src/main/resources/application-test.properties deleted file mode 100644 index 4e3063e4..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/application-test.properties +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Datasource configuration -quarkus.datasource.db-kind = h2 -quarkus.datasource.jdbc.url=jdbc:h2:mem:default;DB_CLOSE_DELAY=-1;INIT=CREATE TYPE IF NOT EXISTS "JSONB" AS json; - -quarkus.hibernate-orm.dialect=org.hibernate.dialect.H2Dialect -quarkus.hibernate-orm.log.sql=true -quarkus.flyway.clean-at-start=true -quarkus.flyway.locations=db/migration,db/testing - -# Disable security -quarkus.oidc.enabled=false - -# Disable OpenAPI/Swagger -quarkus.smallrye-openapi.enable=false -quarkus.swagger-ui.enable=false - -# Disable OpenDC web UI and runner -quarkus.opendc-ui.include=false -quarkus.opendc-runner.include=false - -# Create new tables and fill them -quarkus.hibernate-orm.database.generation=drop-and-create -quarkus.hibernate-orm.sql-load-script=load_data.sql diff --git a/opendc-web/opendc-web-server/src/main/resources/application.properties b/opendc-web/opendc-web-server/src/main/resources/application.properties deleted file mode 100644 index 0f47db30..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/application.properties +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2022 AtLarge Research -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Enable CORS -quarkus.http.cors=true -quarkus.http.cors.origins=http://localhost:3000,https://opendc.org - -# Security -quarkus.oidc.enabled=${opendc.security.enabled} - -# Runner logging -quarkus.log.category."org.opendc".level=ERROR -quarkus.log.category."org.opendc.web".level=INFO -quarkus.log.category."org.apache".level=WARN - -# OpenAPI and Swagger -quarkus.smallrye-openapi.info-title=OpenDC REST API -%dev.quarkus.smallrye-openapi.info-title=OpenDC REST API (development) -quarkus.smallrye-openapi.info-version=2.1-rc1 -quarkus.smallrye-openapi.info-description=OpenDC is an open-source datacenter simulator for education, featuring real-time online collaboration, diverse simulation models, and detailed performance feedback statistics. -quarkus.smallrye-openapi.info-contact-email=opendc@atlarge-research.com -quarkus.smallrye-openapi.info-contact-name=OpenDC Support -quarkus.smallrye-openapi.info-contact-url=https://opendc.org -quarkus.smallrye-openapi.info-license-name=MIT -quarkus.smallrye-openapi.info-license-url=https://github.com/atlarge-research/opendc/blob/master/LICENSE.txt - -quarkus.swagger-ui.path=docs -quarkus.swagger-ui.always-include=true - -# Flyway database migrations -quarkus.flyway.baseline-on-migrate=true -quarkus.flyway.migrate-at-start=true diff --git a/opendc-web/opendc-web-server/src/main/resources/hypersistence-utils.properties b/opendc-web/opendc-web-server/src/main/resources/hypersistence-utils.properties deleted file mode 100644 index 451ce2d8..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/hypersistence-utils.properties +++ /dev/null @@ -1 +0,0 @@ -hypersistence.utils.jackson.object.mapper=org.opendc.web.server.util.QuarkusObjectMapperSupplier diff --git a/opendc-web/opendc-web-server/src/main/resources/load_data.sql b/opendc-web/opendc-web-server/src/main/resources/load_data.sql deleted file mode 100644 index 72396cef..00000000 --- a/opendc-web/opendc-web-server/src/main/resources/load_data.sql +++ /dev/null @@ -1,124 +0,0 @@ - --- Insert data - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) - VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 1', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 1); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 1, 'test_user_1'); - --- Add test user 2 as a viewer for project 1 - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('VIEWER', 1, 'test_user_2'); - --- Add test user 3 as an editor for project 1 - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('EDITOR', 1, 'test_user_3'); - --- Create a project for test user 2 - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 2', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 2); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 2, 'test_user_2'); - --- Create three projects for test user 3. User 3 has multiple projects to test getAll - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 3', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 3); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 3, 'test_user_3'); - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 4', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 4); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 4, 'test_user_3'); - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project 5', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 5); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 5, 'test_user_3'); - --- Project to delete - -INSERT INTO PROJECT (created_at, name, portfolios_created, scenarios_created, topologies_created, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Project Delete', 0, 0, 0, '2024-03-01T15:31:41.579969Z', 6); - -INSERT INTO PROJECTAUTHORIZATION (role, project_id, user_name) -VALUES ('OWNER', 6, 'test_user_1'); - --- -------------------------------------------------------------------------------- --- PortFolios --- -------------------------------------------------------------------------------- - --- Add Portfolio to project 1 -INSERT INTO PORTFOLIO (name, number, project_id, targets, id) -VALUES ('Test PortFolio Base', 1, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 1); - -INSERT INTO PORTFOLIO (name, number, project_id, targets, id) -VALUES ('Test PortFolio Delete', 2, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 2); - -INSERT INTO PORTFOLIO (name, number, project_id, targets, id) -VALUES ('Test PortFolio DeleteEditor', 3, 1, '{"metrics": [], "repeats":1}' FORMAT JSON, 3); - -UPDATE Project p -SET p.portfolios_created = 3, p.updated_at = '2024-03-01T15:31:41.579969Z' -WHERE p.id = 1; - --- -------------------------------------------------------------------------------- --- Topologies --- -------------------------------------------------------------------------------- - -INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testUpdate', 1, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 1); - -INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteAsEditor', 2, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 2); - -INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDelete', 3, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 3); - -INSERT INTO TOPOLOGY (created_at, name, number, project_id, rooms, updated_at, id) -VALUES ('2024-03-01T15:31:41.579969Z', 'Test Topology testDeleteUsed', 4, 1, '[]' FORMAT JSON, '2024-03-01T15:31:41.579969Z', 4); - -UPDATE Project p -SET p.topologies_created = 4, p.updated_at = '2024-03-01T15:31:41.579969Z' -WHERE p.id = 1; - --- -------------------------------------------------------------------------------- --- Traces --- -------------------------------------------------------------------------------- - -INSERT INTO TRACE (id, name, type) -VALUES ('bitbrains-small', 'Bitbrains Small', 'small'); - --- -------------------------------------------------------------------------------- --- Scenario --- -------------------------------------------------------------------------------- - -INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) -VALUES ('Test Scenario testDelete', 1, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 1, 1.0, 'bitbrains-small', 1); - -INSERT INTO SCENARIO (name, number, phenomena, portfolio_id, project_id, scheduler_name, topology_id, sampling_fraction, trace_id, id) -VALUES ('Test Scenario testDeleteUsed', 2, '{"failures": false, "interference": false}' FORMAT JSON, 1, 1, 'test', 4, 1.0, 'bitbrains-small', 2); - - -UPDATE Project p -SET p.scenarios_created = 2, p.updated_at = '2024-03-01T15:31:41.579969Z' -WHERE p.id = 1; - --- -------------------------------------------------------------------------------- --- Job --- -------------------------------------------------------------------------------- - -INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) -VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 1); - -INSERT INTO JOB (scenario_id, created_by, created_at, repeats, updated_at, state, runtime, results, id) -VALUES (1, 'test_user_1', '2024-03-01T15:31:41.579969Z', 1, '2024-03-01T15:31:41.579969Z', 'PENDING', 1, '{}' FORMAT JSON, 2); diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java deleted file mode 100644 index f52ede3a..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/SchedulerResourceTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest; - -import static io.restassured.RestAssured.given; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link SchedulerResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(SchedulerResource.class) -public final class SchedulerResourceTest { - /** - * Test to verify whether we can obtain all schedulers. - */ - @Test - public void testGetSchedulers() { - given().get().then().statusCode(200); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java deleted file mode 100644 index 9da26059..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/TraceResourceTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest; - -import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link TraceResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(TraceResource.class) -public final class TraceResourceTest { - /** - * Test that tries to obtain all traces. - */ - @Test - public void testGetAllEmpty() { - when().get().then().statusCode(200); - } - - /** - * Test that tries to obtain a non-existent trace. - */ - @Test - public void testGetNonExisting() { - when().get("/unknown").then().statusCode(404); - } - - /** - * Test that tries to obtain an existing trace. - */ - @Test - public void testGetExisting() { - when().get("/bitbrains-small") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("Bitbrains Small")); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java deleted file mode 100644 index 09f60c0a..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/runner/JobResourceTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.runner; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; -import org.opendc.web.proto.JobState; - -/** - * Test suite for {@link JobResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(JobResource.class) -public final class JobResourceTest { - /** - * Test that tries to query the pending jobs without token. - */ - @Test - public void testQueryWithoutToken() { - given().get().then().statusCode(401); - } - - /** - * Test that tries to query the pending jobs for a user. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testQueryInvalidScope() { - given().get().then().statusCode(403); - } - - /** - * Test that tries to query the pending jobs for a runner. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testQuery() { - given().get().then().statusCode(200).contentType(ContentType.JSON).body("get(0).state", equalTo("PENDING")); - } - - /** - * Test that tries to obtain a non-existent job. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetNonExisting() { - given().get("/0").then().statusCode(404); - } - - /** - * Test that tries to obtain a job. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetExisting() { - given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); - } - - /** - * Test that tries to update a non-existent job. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testUpdateNonExistent() { - given().body(new org.opendc.web.proto.runner.Job.Update(JobState.PENDING, 0, null)) - .contentType(ContentType.JSON) - .when() - .post("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to update a job. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testUpdateState() { - given().body(new org.opendc.web.proto.runner.Job.Update(JobState.CLAIMED, 0, null)) - .contentType(ContentType.JSON) - .when() - .post("/2") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("state", equalTo(JobState.CLAIMED.toString())); - } - - /** - * Test that tries to update a job with invalid input. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testUpdateInvalidInput() { - given().body("{ \"test\": \"test\" }") - .contentType(ContentType.JSON) - .when() - .post("/1") - .then() - .statusCode(400) - .contentType(ContentType.JSON); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java deleted file mode 100644 index f23b4fc4..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioResourceTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import java.util.Set; -import org.junit.jupiter.api.Test; -import org.opendc.web.proto.Targets; - -/** - * Test suite for {@link PortfolioResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(PortfolioResource.class) -public final class PortfolioResourceTest { - /** - * Test that tries to obtain the list of all portfolios belonging to a project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetAllForProject() { - given().pathParam("project", 1).when().get().then().statusCode(200); - } - - /** - * Test that tries to obtain the list of all portfolios belonging to a project - * without authorization. - * - * TODO: Why is this an empty list, and not a 403 message? - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetAllForProjectNoAuthorization() { - given().pathParam("project", 1).when().get().then().statusCode(200); - } - - /** - * Test that tries to create a portfolio for a project that exists and user has permission. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreate() { - given().pathParam("project", "1") - .body(new org.opendc.web.proto.user.Portfolio.Create("Test Portfolio New", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("Test Portfolio New")); - } - - /** - * Test that tries to create a topology for a project that does not exist. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateNonExistent() { - given().pathParam("project", "0") - .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404); - } - - /** - * Test that tries to create a portfolio for a project that does exist but the user does not have permission. - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testCreateViewer() { - given().pathParam("project", "1") - .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(403); - } - - /** - * Test that tries to create a portfolio for a project that does exist but the user does not have permission. - * TODO: This should return 403 but does not because there is no user class - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateNotPermitted() { - given().pathParam("project", "3") - .body(new org.opendc.web.proto.user.Portfolio.Create("test", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404); - } - - /** - * Test to create a portfolio with an empty body. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateEmpty() { - given().pathParam("project", "1") - .body("{}") - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400); - } - - /** - * Test to create a portfolio with a blank name. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateBlankName() { - given().pathParam("project", "1") - .body(new org.opendc.web.proto.user.Portfolio.Create("", new Targets(Set.of(), 1))) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400); - } - - /** - * Test that tries to obtain a portfolio without token. - */ - @Test - public void testGetWithoutToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(401); - } - - /** - * Test that tries to obtain a portfolio with an invalid scope. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetInvalidToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(403); - } - - /** - * Test that tries to obtain a non-existent portfolio. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetNonExisting() { - given().pathParam("project", "1").when().get("/0").then().statusCode(404); - } - - /** - * Test that tries to obtain a portfolio for a non-existent project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetNonExistingProject() { - given().pathParam("project", "0").when().get("/1").then().statusCode(404); - } - - /** - * Test that tries to obtain a portfolio. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetExisting() { - given().pathParam("project", "1") - .when() - .get("/1") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("id", equalTo(1)); - } - - /** - * Test to delete a non-existent portfolio. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteNonExistent() { - given().pathParam("project", "1").when().delete("/0").then().statusCode(404); - } - - /** - * Test to delete a portfolio on a non-existent project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteNonExistentProject() { - given().pathParam("project", "0").when().delete("/1").then().statusCode(404); - } - - /** - * Test to delete a portfolio. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDelete() { - given().pathParam("project", "1").when().delete("/2").then().statusCode(200); - } - - /** - * Test to delete a portfolio as an editor. - */ - @Test - @TestSecurity( - user = "test_user_3", - roles = {"openid"}) - public void testDeleteEditor() { - given().pathParam("project", "1").when().delete("/3").then().statusCode(200); - } - - /** - * Test to delete a portfolio as a viewer. - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testDeleteAsViewer() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(403); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java deleted file mode 100644 index 270dbae9..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/PortfolioScenarioResourceTest.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; -import org.opendc.web.proto.OperationalPhenomena; -import org.opendc.web.proto.Workload; -import org.opendc.web.proto.user.Scenario; - -/** - * Test suite for {@link PortfolioScenarioResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(PortfolioScenarioResource.class) -public final class PortfolioScenarioResourceTest { - /** - * Test that tries to obtain a portfolio without token. - */ - @Test - public void testGetWithoutToken() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .when() - .get() - .then() - .statusCode(401); - } - - /** - * Test that tries to obtain a portfolio with an invalid scope. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetInvalidToken() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .when() - .get() - .then() - .statusCode(403); - } - - /** - * Test that tries to obtain a scenario without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetUnauthorized() { - given().pathParam("project", "2") - .pathParam("portfolio", "1") - .when() - .get() - .then() - .statusCode(200) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to obtain a scenario. - * TODO: shouldn't this be all scenarios? - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGet() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .when() - .get() - .then() - .statusCode(200); - } - - /** - * Test that tries to create a scenario for a portfolio that does not exist in a project that can be accessed. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateNonExistent() { - given().pathParam("project", "1") - .pathParam("portfolio", "0") - .body(new Scenario.Create( - "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to create a scenario for a portfolio without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateUnauthorized() { - given().pathParam("project", "2") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to create a scenario for a portfolio as a viewer. - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testCreateAsViewer() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "test", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(403) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to create a scenario for a portfolio. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreate() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "Test Scenario New", - new Workload.Spec("bitbrains-small", 1.0), - 1, - new OperationalPhenomena(false, false), - "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("Test Scenario New")); - } - - /** - * Test to create a project with an empty body. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateEmpty() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body("{}") - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); - } - - /** - * Test to create a project with a blank name. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateBlankName() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "", new Workload.Spec("test", 1.0), 1, new OperationalPhenomena(false, false), "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to create a scenario for a portfolio with an unknown Topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateUnknownTopology() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "test", - new Workload.Spec("bitbrains-small", 1.0), - -1, - new OperationalPhenomena(false, false), - "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to create a scenario for a portfolio with an unknown Trace. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateUnknownTrace() { - given().pathParam("project", "1") - .pathParam("portfolio", "1") - .body(new Scenario.Create( - "test", new Workload.Spec("unknown", 1.0), 1, new OperationalPhenomena(false, false), "test")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400) - .contentType(ContentType.JSON); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java deleted file mode 100644 index 450c0c0c..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ProjectResourceTest.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.given; -import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.*; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; - -/** - * Test suite for [ProjectResource]. - */ -@QuarkusTest -@TestHTTPEndpoint(ProjectResource.class) -public final class ProjectResourceTest { - /** - * Test that tries to obtain all projects without token. - */ - @Test - public void testGetAllWithoutToken() { - when().get().then().statusCode(401); - } - - /** - * Test that tries to obtain all projects with an invalid scope. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetAllWithInvalidScope() { - when().get().then().statusCode(403); - } - - /** - * Test that tries to obtain when no projects have yet been made. - */ - @Test - @TestSecurity( - user = "test_user_4", - roles = {"openid"}) - public void testGetAllWithNoAvailableProjects() { - when().get().then().statusCode(200).contentType(ContentType.JSON).body("", empty()); - } - - /** - * Test that tries to obtain all project for a user. - */ - @Test - @TestSecurity( - user = "test_user_3", - roles = {"openid"}) - public void testGetAll() { - given().get().then().statusCode(200).contentType(ContentType.JSON).body("", hasSize(4)); - } - - /** - * Test that tries to obtain a non-existent project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetNonExisting() { - when().get("/0").then().statusCode(404).contentType(ContentType.JSON); - } - - /** - * Test that tries to obtain a project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetExisting() { - // Try to get the project - given().get("/1").then().statusCode(200).contentType(ContentType.JSON).body("id", equalTo(1)); - } - - /** - * Test that tries to create a project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreate() { - given().body(new org.opendc.web.proto.user.Project.Create("Test Project New")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("Test Project New")); - } - - /** - * Test to create a project with an empty body. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateEmpty() { - given().body("{}").contentType(ContentType.JSON).when().post().then().statusCode(400); - } - - /** - * Test to create a project with a blank name. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateBlankName() { - given().body(new org.opendc.web.proto.user.Project.Create("")) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400); - } - - /** - * Test to delete a project that is owned by the user. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDelete() { - given().delete("/6").then().statusCode(200); - } - - /** - * Test to delete a non-existent project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteNonExistent() { - when().delete("/0").then().statusCode(404); - } - - /** - * Test to delete a project which is not connected to the user. - * test_user_3 is not connected to project 1. - */ - @Test - @TestSecurity( - user = "test_user_3", - roles = {"openid"}) - public void testDeleteNotConnected() { - when().delete("/1").then().statusCode(403); - } - - /** - * Test to delete a project which the user does not own. - * project 1 is owned by test_user_1, test_user_2 is a viewer - * should not be able to delete it - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testDeleteNonOwner() { - when().delete("/1").then().statusCode(403); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java deleted file mode 100644 index d81f9655..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/ScenarioResourceTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link ScenarioResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(ScenarioResource.class) -public final class ScenarioResourceTest { - /** - * Test that tries to obtain all scenarios belonging to a project without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetAllUnauthorized() { - given().pathParam("project", "2").when().get().then().statusCode(404); - } - - /** - * Test that tries to obtain all scenarios belonging to a project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200); - } - - /** - * Test that tries to obtain a scenario without token. - */ - @Test - public void testGetWithoutToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(401); - } - - /** - * Test that tries to obtain a scenario with an invalid scope. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetInvalidToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(403); - } - - /** - * Test that tries to obtain a non-existent scenario. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetNonExisting() { - given().pathParam("project", "1") - .when() - .get("/0") - .then() - .statusCode(404) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to obtain a scenario when it does not have authority to get to the project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetExistingUnauthorized() { - given().pathParam("project", "2") - .when() - .get("/1") - .then() - .statusCode(404) - .contentType(ContentType.JSON); - } - - /** - * Test that tries to obtain a scenario. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetExisting() { - given().pathParam("project", "1") - .when() - .get("/1") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("id", equalTo(1)); - } - - /** - * Test to delete a non-existent scenario. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteNonExistent() { - given().pathParam("project", "1").when().delete("/0").then().statusCode(404); - } - - /** - * Test to delete a scenario without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteUnauthorized() { - given().pathParam("project", "2").when().delete("/1").then().statusCode(404); - } - - /** - * Test to delete a scenario as a viewer. - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testDeleteAsViewer() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(403); - } - - /** - * Test to delete a scenario. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDelete() { - given().pathParam("project", "1") - .when() - .delete("/1") - .then() - .statusCode(200) - .contentType(ContentType.JSON); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java deleted file mode 100644 index 277376e5..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/TopologyResourceTest.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import java.util.List; -import org.junit.jupiter.api.Test; -import org.opendc.web.proto.user.Topology; - -/** - * Test suite for {@link TopologyResource}. - */ -@QuarkusTest -@TestHTTPEndpoint(TopologyResource.class) -public final class TopologyResourceTest { - /** - * Test that tries to obtain the list of topologies of a project without proper authorization. - */ - @Test - @TestSecurity( - user = "test_user_4", - roles = {"openid"}) - public void testGetAllWithoutAuth() { - given().pathParam("project", "1") - .when() - .get() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body(equalTo("[]")); - } - - /** - * Test that tries to obtain the list of topologies belonging to a project. - * TODO: check if any topology comes back - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetAll() { - given().pathParam("project", "1").when().get().then().statusCode(200); - } - - /** - * Test that tries to create a topology for a project that does not exist. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateNonExistent() { - given().pathParam("project", "0") - .body(new Topology.Create("test", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404); - } - - /** - * Test that tries to create a topology for a project while not authorized. - * TODO: should probably return 403, but this does not work in the current system - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateUnauthorized() { - given().pathParam("project", "2") - .body(new Topology.Create("test", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(404); - } - - /** - * Test that tries to create a topology for a project. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreate() { - given().pathParam("project", "1") - .body(new Topology.Create("Test Topology New", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("name", equalTo("Test Topology New")); - } - - /** - * Test to create a topology with an empty body. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateEmpty() { - given().pathParam("project", "1") - .body("{}") - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400); - } - - /** - * Test to create a topology with a blank name. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testCreateBlankName() { - given().pathParam("project", "1") - .body(new Topology.Create("", List.of())) - .contentType(ContentType.JSON) - .when() - .post() - .then() - .statusCode(400); - } - - /** - * Test that tries to obtain a topology without token. - */ - @Test - public void testGetWithoutToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(401); - } - - /** - * Test that tries to obtain a topology with an invalid scope. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"runner"}) - public void testGetInvalidToken() { - given().pathParam("project", "1").when().get("/1").then().statusCode(403); - } - - /** - * Test that tries to obtain a non-existent topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetNonExisting() { - given().pathParam("project", "1").when().get("/0").then().statusCode(404); - } - - /** - * Test that tries to obtain a topology without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetUnauthorized() { - given().pathParam("project", "2").when().get("/1").then().statusCode(404); - } - - /** - * Test that tries to obtain a topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testGetExisting() { - given().pathParam("project", "1") - .when() - .get("/1") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("id", equalTo(1)); - } - - /** - * Test to delete a non-existent topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testUpdateNonExistent() { - given().pathParam("project", "1") - .body(new Topology.Update(List.of())) - .contentType(ContentType.JSON) - .when() - .put("/0") - .then() - .statusCode(404); - } - - /** - * Test to delete a topology without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testUpdateUnauthorized() { - given().pathParam("project", "2") - .body(new Topology.Update(List.of())) - .contentType(ContentType.JSON) - .when() - .put("/1") - .then() - .statusCode(404); - } - - /** - * Test to update a topology as a viewer. - * TODO: should return 403, but currently returns 404 - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testUpdateAsViewer() { - given().pathParam("project", "1") - .body(new Topology.Update(List.of())) - .contentType(ContentType.JSON) - .when() - .put("/1") - .then() - .statusCode(403) - .contentType(ContentType.JSON); - } - - /** - * Test to update a topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testUpdate() { - given().pathParam("project", "1") - .body(new Topology.Update(List.of())) - .contentType(ContentType.JSON) - .when() - .put("/1") - .then() - .statusCode(200); - } - - /** - * Test to delete a non-existent topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteNonExistent() { - given().pathParam("project", "1").when().delete("/0").then().statusCode(404); - } - - /** - * Test to delete a topology without authorization. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteUnauthorized() { - given().pathParam("project", "2").when().delete("/1").then().statusCode(404); - } - - /** - * Test to delete a topology as a viewer. - */ - @Test - @TestSecurity( - user = "test_user_2", - roles = {"openid"}) - public void testDeleteAsViewer() { - given().pathParam("project", "1").when().delete("/1").then().statusCode(403); - } - - /** - * Test to delete a topology as a viewer. - */ - @Test - @TestSecurity( - user = "test_user_3", - roles = {"openid"}) - public void testDeleteAsEditor() { - given().pathParam("project", "1").when().delete("/2").then().statusCode(200); - } - - /** - * Test to delete a topology. - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDelete() { - given().pathParam("project", "1").when().delete("/3").then().statusCode(200); - } - - /** - * Test to delete a topology that is still being used by a scenario. - * TODO: fix later - */ - @Test - @TestSecurity( - user = "test_user_1", - roles = {"openid"}) - public void testDeleteUsed() { - given().pathParam("project", "1") - .when() - .delete("/4") // Topology 1 is still used by scenario 1 and 2 - .then() - .statusCode(403) - .contentType(ContentType.JSON); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java deleted file mode 100644 index 6dcb3b4d..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/rest/user/UserResourceTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.rest.user; - -import static io.restassured.RestAssured.when; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.greaterThan; - -import io.quarkus.test.common.http.TestHTTPEndpoint; -import io.quarkus.test.junit.QuarkusTest; -import io.quarkus.test.security.TestSecurity; -import io.restassured.http.ContentType; -import org.junit.jupiter.api.Test; - -/** - * Test suite for [UserResource]. - */ -@QuarkusTest -@TestHTTPEndpoint(UserResource.class) -public final class UserResourceTest { - /** - * Test that tries to obtain the profile of the active user. - */ - @Test - @TestSecurity( - user = "testUser", - roles = {"openid"}) - public void testMe() { - when().get("me") - .then() - .statusCode(200) - .contentType(ContentType.JSON) - .body("userId", equalTo("testUser")) - .body("accounting.simulationTime", equalTo(0)) - .body("accounting.simulationTimeBudget", greaterThan(0)); - } - - /** - * Test that tries to obtain the profile of the active user without authorization. - */ - @Test - public void testMeUnauthorized() { - when().get("me").then().statusCode(401); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java deleted file mode 100644 index f6d871c0..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/JobServiceTest.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.service; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; - -import io.quarkus.test.junit.QuarkusTest; -import java.time.Instant; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.opendc.web.proto.JobState; -import org.opendc.web.server.model.Job; - -/** - * Test suite for the {@link JobService}. - */ -@QuarkusTest -public class JobServiceTest { - /** - * The {@link JobService} instance under test. - */ - private JobService service; - - /** - * The mock {@link UserAccountingService}. - */ - private UserAccountingService mockAccountingService; - - @BeforeEach - public void setUp() { - mockAccountingService = Mockito.mock(UserAccountingService.class); - service = new JobService(mockAccountingService); - } - - @Test - public void testUpdateInvalidTransition() { - Job job = new Job(null, "test", Instant.now(), 1); - job.state = JobState.RUNNING; - - assertThrows(IllegalArgumentException.class, () -> service.updateJob(job, JobState.CLAIMED, 0, null)); - - Mockito.verifyNoInteractions(mockAccountingService); - } - - @Test - public void testUpdateNoBudget() { - Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1)); - job.state = JobState.RUNNING; - - Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt())) - .thenReturn(true); - Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any()); - - service.updateJob(job, JobState.RUNNING, 0, null); - - Mockito.verify(job).updateAtomically(eq(JobState.FAILED), any(), anyInt(), any()); - } - - @Test - public void testUpdateNoBudgetWhenFinishing() { - Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1)); - job.state = JobState.RUNNING; - - Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt())) - .thenReturn(true); - Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any()); - - service.updateJob(job, JobState.FINISHED, 0, null); - - Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any()); - } - - @Test - public void testUpdateSuccess() { - Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1)); - job.state = JobState.RUNNING; - - Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt())) - .thenReturn(false); - Mockito.doReturn(true).when(job).updateAtomically(any(), any(), anyInt(), any()); - - service.updateJob(job, JobState.FINISHED, 0, null); - - Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any()); - } - - @Test - public void testUpdateConflict() { - Job job = Mockito.spy(new Job(null, "test", Instant.now(), 1)); - job.state = JobState.RUNNING; - - Mockito.when(mockAccountingService.consumeSimulationBudget(any(), anyInt())) - .thenReturn(false); - Mockito.doReturn(false).when(job).updateAtomically(any(), any(), anyInt(), any()); - - assertThrows(IllegalStateException.class, () -> service.updateJob(job, JobState.FINISHED, 0, null)); - - Mockito.verify(job).updateAtomically(eq(JobState.FINISHED), any(), anyInt(), any()); - } -} diff --git a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java b/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java deleted file mode 100644 index 91e3eb66..00000000 --- a/opendc-web/opendc-web-server/src/test/java/org/opendc/web/server/service/UserAccountingServiceTest.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2023 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package org.opendc.web.server.service; - -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; - -import io.quarkus.panache.mock.PanacheMock; -import io.quarkus.test.junit.QuarkusTest; -import jakarta.persistence.EntityExistsException; -import java.time.Duration; -import java.time.LocalDate; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; -import org.opendc.web.server.model.UserAccounting; - -/** - * Test suite for the {@link UserAccountingService}. - */ -@QuarkusTest -public class UserAccountingServiceTest { - /** - * The {@link UserAccountingService} instance under test. - */ - private UserAccountingService service; - - /** - * The user id to test with - */ - private final String userId = "test"; - - @BeforeEach - public void setUp() { - PanacheMock.mock(UserAccounting.class); - service = new UserAccountingService(Duration.ofHours(1)); - } - - @Test - public void testGetUserDoesNotExist() { - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null); - - var accounting = service.getAccounting(userId); - - assertTrue(accounting.getPeriodEnd().isAfter(LocalDate.now())); - assertEquals(0, accounting.getSimulationTime()); - } - - @Test - public void testGetUserDoesExist() { - var now = LocalDate.now(); - var periodEnd = now.plusMonths(1); - - var mockAccounting = new UserAccounting(userId, periodEnd, 3600); - mockAccounting.simulationTime = 32; - - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting); - - var accounting = service.getAccounting(userId); - - assertAll( - () -> assertEquals(periodEnd, accounting.getPeriodEnd()), - () -> assertEquals(32, accounting.getSimulationTime()), - () -> assertEquals(3600, accounting.getSimulationTimeBudget())); - } - - @Test - public void testHasBudgetUserDoesNotExist() { - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null); - - assertTrue(service.hasSimulationBudget(userId)); - } - - @Test - public void testHasBudget() { - var periodEnd = LocalDate.now().plusMonths(2); - - var mockAccounting = new UserAccounting(userId, periodEnd, 3600); - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting); - - assertTrue(service.hasSimulationBudget(userId)); - } - - @Test - public void testHasBudgetExceededButPeriodExpired() { - var periodEnd = LocalDate.now().minusMonths(2); - - var mockAccounting = new UserAccounting(userId, periodEnd, 3600); - mockAccounting.simulationTime = 3900; - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting); - - assertTrue(service.hasSimulationBudget(userId)); - } - - @Test - public void testHasBudgetPeriodExpired() { - var periodEnd = LocalDate.now().minusMonths(2); - - var mockAccounting = new UserAccounting(userId, periodEnd, 3600); - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting); - - assertTrue(service.hasSimulationBudget(userId)); - } - - @Test - public void testHasBudgetExceeded() { - var periodEnd = LocalDate.now().plusMonths(1); - - var mockAccounting = new UserAccounting(userId, periodEnd, 3600); - mockAccounting.simulationTime = 3900; - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(mockAccounting); - - assertFalse(service.hasSimulationBudget(userId)); - } - - @Test - public void testConsumeBudgetNewUser() { - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null); - Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt())) - .thenAnswer((i) -> { - var accounting = new UserAccounting(i.getArgument(0), i.getArgument(1), i.getArgument(2)); - accounting.simulationTime = i.getArgument(3); - return accounting; - }); - - assertFalse(service.consumeSimulationBudget(userId, 10)); - } - - @Test - public void testConsumeBudgetNewUserExceeded() { - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null); - Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt())) - .thenAnswer((i) -> { - var accounting = new UserAccounting(i.getArgument(0), i.getArgument(1), i.getArgument(2)); - accounting.simulationTime = i.getArgument(3); - return accounting; - }); - - assertTrue(service.consumeSimulationBudget(userId, 4000)); - } - - @Test - public void testConsumeBudgetNewUserConflict() { - var periodEnd = LocalDate.now().plusMonths(1); - var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600)); - - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(null).thenReturn(accountingMock); - Mockito.when(UserAccounting.create(anyString(), any(), anyInt(), anyInt())) - .thenThrow(new EntityExistsException()); - Mockito.when(accountingMock.consumeBudget(anyInt())).thenAnswer((i) -> { - accountingMock.simulationTime += i.getArgument(0); - return true; - }); - - assertFalse(service.consumeSimulationBudget(userId, 10)); - } - - @Test - public void testConsumeBudgetResetSuccess() { - var periodEnd = LocalDate.now().minusMonths(2); - var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600)); - accountingMock.simulationTime = 3900; - - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(accountingMock); - Mockito.when(accountingMock.resetBudget(any(), anyInt())).thenAnswer((i) -> { - accountingMock.periodEnd = i.getArgument(0); - accountingMock.simulationTime += i.getArgument(1); - return true; - }); - - assertTrue(service.consumeSimulationBudget(userId, 4000)); - } - - @Test - public void testInfiniteConflict() { - var periodEnd = LocalDate.now().plusMonths(1); - var accountingMock = Mockito.spy(new UserAccounting(userId, periodEnd, 3600)); - - Mockito.when(UserAccounting.findByUser(userId)).thenReturn(accountingMock); - Mockito.when(accountingMock.consumeBudget(anyInt())).thenAnswer((i) -> { - accountingMock.simulationTime += i.getArgument(0); - return false; - }); - - assertThrows(IllegalStateException.class, () -> service.consumeSimulationBudget(userId, 10)); - } -} -- cgit v1.2.3