Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions Middleware/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
Expand All @@ -118,6 +113,9 @@
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/test/resources</directory>
</resource>
<resource>
<directory>${project.basedir}</directory>
<includes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@
* @since 2025-01-01
*/


@SpringBootApplication
@ComponentScan(basePackages = { "ca.concordia.encs.citydata.core.controllers",
"ca.concordia.encs.citydata.core.configs", "ca.concordia.encs.citydata.datastores",
"ca.concordia.encs.citydata.services", "ca.concordia.encs.citydata.core.utils",
"ca.concordia.encs.citydata.core" })
"ca.concordia.encs.citydata.core", "ca.concordia.encs.citydata.producers" })
@EnableConfigurationProperties(RsaKeyProperties.class)
public class Application {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
import com.google.gson.JsonParser;

import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.implementations.ExceptionProducer;
import ca.concordia.encs.citydata.datastores.InMemoryDataStore;
import ca.concordia.encs.citydata.producers.ExceptionProducer;
import ca.concordia.encs.citydata.runners.SequentialRunner;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@
*/

public class MiddlewareException extends RuntimeException {

public MiddlewareException(String message) {
super(message);
}

public MiddlewareException(final String message, final Exception cause) {
super(message, cause);
}

public static class InvalidProducerException extends MiddlewareException {
public InvalidProducerException(String producerName) {
public InvalidProducerException(String producerName, final Exception e) {
super("Producer " + producerName
+ " was not found. Please check whether the fully-qualified name is correct and try again.");
+ " was not found. Please check whether the fully-qualified name is correct and try again.", e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,24 @@
* @since 2025-05-27
*/

public abstract class AbstractProducer<E> extends AbstractEntity implements IProducer<E> {

public sealed abstract class AbstractProducer<E> extends AbstractEntity implements IProducer<E> permits JSONProducer,
CSVProducer, ExceptionProducer, FirebaseProducer, PortfolioManagerProducer, PortfolioManagerMetadataProducer {
private String filePath;
private RequestOptions fileOptions;
private IOperation<E> operation;
private final Set<IRunner> runners = new HashSet<>();
private ArrayList<E> result = new ArrayList<>();

public AbstractProducer(final String filePath, final RequestOptions fileOptions) {
this.filePath = filePath;
this.fileOptions = fileOptions;
this.setMetadata("role", "producer");
}

public AbstractProducer(final String filePath) {
this(filePath, null);
}

public String getFilePath() {
return filePath;
}
Expand All @@ -70,8 +80,13 @@ public Set<IRunner> getRunners() {
return runners;
}

public void setResult(ArrayList<E> result) {
this.result = result;
public void setResult(ArrayList<?> result) {
this.result = (ArrayList<E>) result;
}

@Override
public ArrayList<E> getResult() {
return this.result;
}

public AbstractProducer() {
Expand Down Expand Up @@ -113,11 +128,6 @@ public void applyOperation() {
this.notifyObservers();
}

@Override
public ArrayList<E> getResult() {
return this.result;
}

public boolean isEmpty() {
return this.result == null || this.result.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ca.concordia.encs.citydata.producers.base;
package ca.concordia.encs.citydata.core.implementations;

import java.io.IOException;
import java.io.OutputStream;
Expand All @@ -8,7 +8,6 @@

import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.exceptions.MiddlewareException;
import ca.concordia.encs.citydata.core.implementations.AbstractProducer;
import ca.concordia.encs.citydata.core.utils.RequestOptions;

/**
Expand All @@ -18,18 +17,18 @@
* @since 2024-12-01
*/

public class CSVProducer extends AbstractProducer<String> implements IProducer<String> {
public non-sealed class CSVProducer extends AbstractProducer<String> implements IProducer<String> {

public CSVProducer(String filePath, RequestOptions fileOptions) {
this.setFilePath(filePath);
this.setFileOptions(fileOptions);
public CSVProducer(final String filePath, final RequestOptions fileOptions) {
super(filePath, fileOptions);
}

// I added the error handling to ensure I actually read my local file
public CSVProducer(final String filePath) {
super(filePath);
}

@Override
public void fetch() {

try (OutputStream outputStream = this.fetchFromPath();
OutputStreamWriter writer = new OutputStreamWriter(outputStream)) {
writer.flush(); // Ensure all data is written to the stream
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package ca.concordia.encs.citydata.producers;
package ca.concordia.encs.citydata.core.implementations;

import java.util.ArrayList;

import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.implementations.AbstractProducer;

/**
* This producer was created for the sole purpose of returning Exceptions when
Expand All @@ -14,11 +13,14 @@
* @since 2025-01-01
*/

public class ExceptionProducer extends AbstractProducer<String> implements IProducer<String> {
//Need to discuss with Yann, and then probably move this back to producers package

public non-sealed class ExceptionProducer extends AbstractProducer<String> implements IProducer<String> {

public ExceptionProducer(Exception e) {
final ArrayList<String> result = new ArrayList<>();
result.add(e.getMessage());
result.add("caused by: " + e.getCause());
this.setResult(result);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ca.concordia.encs.citydata.producers;
package ca.concordia.encs.citydata.core.implementations;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -13,7 +13,6 @@
import com.google.gson.JsonParser;

import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.implementations.AbstractProducer;
import ca.concordia.encs.citydata.core.utils.RequestOptions;

/***
Expand All @@ -22,7 +21,9 @@
* @since 2024-02-07
*/

public class FirebaseProducer extends AbstractProducer<JsonObject> implements IProducer<JsonObject> {
//Need to discuss with Yann, and then probably move this back to producers package

public non-sealed class FirebaseProducer extends AbstractProducer<JsonObject> implements IProducer<JsonObject> {

private final String nodePath; // Path to the Firebase node to fetch data from

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package ca.concordia.encs.citydata.producers.base;
package ca.concordia.encs.citydata.core.implementations;

import java.io.OutputStream;
import java.util.ArrayList;
Expand All @@ -8,7 +8,6 @@
import com.google.gson.JsonParser;

import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.implementations.AbstractProducer;
import ca.concordia.encs.citydata.core.utils.RequestOptions;

/**
Expand All @@ -18,11 +17,14 @@
* @since 2024-12-01
*/

public class JSONProducer extends AbstractProducer<JsonObject> implements IProducer<JsonObject> {
public non-sealed class JSONProducer extends AbstractProducer<JsonObject> implements IProducer<JsonObject> {

public JSONProducer(String filePath, RequestOptions fileOptions) {
this.setFilePath(filePath);
this.setFileOptions(fileOptions);
public JSONProducer(final String filePath, final RequestOptions fileOptions) {
super(filePath, fileOptions);
}

public JSONProducer(final String filePath) {
super(filePath);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package ca.concordia.encs.citydata.core.implementations;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

import com.google.gson.JsonObject;

import ca.concordia.encs.citydata.core.configs.PortfolioManagerConfig;
import ca.concordia.encs.citydata.core.contracts.IOperation;
import ca.concordia.encs.citydata.core.contracts.IProducer;
import ca.concordia.encs.citydata.core.contracts.IRunner;
import ca.concordia.encs.citydata.core.utils.RequestOptions;

/**
* Producer for fetching Portfolio Manager metadata (account, property, or meter) and forwarding the XML response to the
* PortfolioManagerProducer for further processing.
* @author Minette Zongo
* @since 2026-02-24
*/

//Need to discuss with Yann, and then probably move this back to producers package

public non-sealed class PortfolioManagerMetadataProducer extends AbstractProducer<JsonObject>
implements IProducer<JsonObject> {
private String dataType;
private String accountId;
private String propertyId;
private String meterId;
private IOperation<JsonObject> operation;
private IRunner runnerObserver;

public void setAccountId(String accountId) {
this.accountId = accountId;
}

public void setPropertyId(String propertyId) {
this.propertyId = propertyId;
}

public void setMeterId(String meterId) {
this.meterId = meterId;
}

public void setDataType(String dataType) {
this.dataType = dataType.toUpperCase();
}

@SuppressWarnings("rawtypes")
@Override
public void setOperation(IOperation operation) {
this.operation = operation;
}

@Override
public void addObserver(IRunner aRunner) {
this.runnerObserver = aRunner;
}

@Override
public void fetch() {
if (this.dataType == null) {
throw new RuntimeException(
"'dataType' is required. " + "Valid values: ACCOUNT, PROPERTY_LIST, PROPERTY, METER_LIST, METER");
}

final String endpoint = resolveEndpoint();
final RequestOptions requestOptions = new RequestOptions();
requestOptions.setMethod("GET");
requestOptions.addToHeaders("Authorization", buildBasicAuth());
requestOptions.addToHeaders("Accept", "application/xml");

this.setFilePath(endpoint);
this.setFileOptions(requestOptions);

final String xmlResponse = this.fetchFromPath().toString();

final JsonObject wrapper = new JsonObject();
wrapper.addProperty("xml", xmlResponse);
wrapper.addProperty("endpoint", endpoint);

final ArrayList<JsonObject> result = new ArrayList<>();
result.add(wrapper);
this.setResult(result);
super.addObserver(this.runnerObserver);
super.setOperation(this.operation);
this.applyOperation();
}

private String resolveEndpoint() {
String base = PortfolioManagerConfig.getBaseUrl();
return switch (this.dataType) {
case "ACCOUNT" -> base + "/account";
case "PROPERTY_LIST" -> base + "/account/" + requireId(accountId, "accountId") + "/property/list";
case "PROPERTY" -> base + "/property/" + requireId(propertyId, "propertyId");
case "METER_LIST" -> base + "/property/" + requireId(propertyId, "propertyId") + "/meter/list";
case "METER" -> base + "/meter/" + requireId(meterId, "meterId");
default -> throw new RuntimeException(
"Unknown dataType: " + this.dataType + ". Valid: ACCOUNT, PROPERTY_LIST, PROPERTY, METER_LIST, METER");
};
}

private String buildBasicAuth() {
return "Basic " + java.util.Base64.getEncoder()
.encodeToString((PortfolioManagerConfig.getUsername() + ":" + PortfolioManagerConfig.getPassword())
.getBytes(StandardCharsets.UTF_8));
}

private String requireId(String id, String name) {
if (id == null || id.isBlank()) {
throw new RuntimeException("'" + name + "' is required for dataType: " + this.dataType);
}
return id;
}

}
Loading
Loading