diff --git a/.gitignore b/.gitignore
index a1c2a23..3585c55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,7 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+
+.idea/
+
+target/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..2d3d943
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+FROM postgres
+ENV POSTGRES_PASSWORD admin
+ENV POSTGRES_DB postgres
+COPY sqlscripts/docker_init.sql /docker-entrypoint-initdb.d/
\ No newline at end of file
diff --git a/developer_guide/forecasting_api_doc.txt b/developer_guide/forecasting_api_doc.txt
new file mode 100644
index 0000000..213394f
--- /dev/null
+++ b/developer_guide/forecasting_api_doc.txt
@@ -0,0 +1,28 @@
+1. created a simple spring boot web application using spring intializer website.
+2. using postgres as a datasource hence creating a db to provide as spring datasource. For now
+ only one db is created in the name of the tenant.
+ //TODO In a larger picture we need to serve the api for multiple clients.
+ // TODO Need to discuss with Sebin on how to design services that can serve multiple clients.
+
+ The following is the command is spawn a docker container with container name postgresdb
+ that runs postgres server with user postgres and password admin
+
+ docker container run --name postgresdb -e POSTGRES_PASSWORD=admin -d -p 5432:5432 postgres
+ use the command to check the running containers
+ docker container ls
+
+3. create a sql script that creates a database and a user for the database.
+4. copy the script to the docker container
+ docker cp sqlscripts/forecaster.sql postgresdb:/
+5. enter into the docker container bash
+ docker container exec -it postgresdb bash
+6. run the following command to execute the copied sql
+ psql -U postgres --file forecaster.sql
+7. Spring offers multiple ways to configure datasource beans. one way is to set in the application.properties
+ another way is using java config beans.
+ // TODO Need to discuss with sebin how they configure multiple datasources in their company and in general.
+ for now going with application.properties
+8. set the following properties in application.properties.(Remember that properties file doesn't allow spaces)
+ spring.datasource.url=jdbc:postgresql://localhost:5432/walmartdb
+ spring.datasource.username=walmart
+ spring.datasource.password=walmart@123
diff --git a/init.sh b/init.sh
new file mode 100644
index 0000000..83d49e3
--- /dev/null
+++ b/init.sh
@@ -0,0 +1,5 @@
+docker build -t postgres_forecasting .
+
+docker run -it --name postgresdb -d -p 5432:5432 postgres_forecasting
+
+docker container exec -it postgresdb bash
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5c60267
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,92 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.4.RELEASE
+
+
+ com.example
+ forecasting
+ 0.0.1-SNAPSHOT
+ forecasting
+ Forecasting API
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ 2.3.4.RELEASE
+
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ runtime
+ true
+
+
+ org.postgresql
+ postgresql
+ runtime
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+ org.mindrot
+ jbcrypt
+ 0.4
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/sqlscripts/docker_init.sql b/sqlscripts/docker_init.sql
new file mode 100644
index 0000000..548cf82
--- /dev/null
+++ b/sqlscripts/docker_init.sql
@@ -0,0 +1,25 @@
+create user walmart with password 'walmart@123';
+create database walmartdb with template=template0 owner=walmart;
+\connect walmartdb;
+alter default privileges grant all on tables to walmart;
+alter default privileges grant all on sequences to walmart;
+
+create extension "uuid-ossp";
+
+create table orgs_enabled(
+ org_id integer primary key not null,
+ org_name varchar(30) not null,
+ is_enabled boolean not null default true
+);
+
+create table users (
+ user_id UUID primary key not null,
+ first_name varchar(20) not null,
+ last_name varchar(20),
+ email varchar(30) not null,
+ password text not null,
+ org_id integer not null
+);
+
+alter table users add constraint org_id_fk foreign key (org_id) references orgs_enabled(org_id);
+create sequence org_id_seq increment 1 start 1;
\ No newline at end of file
diff --git a/sqlscripts/forecaster.sql b/sqlscripts/forecaster.sql
new file mode 100644
index 0000000..6429026
--- /dev/null
+++ b/sqlscripts/forecaster.sql
@@ -0,0 +1,28 @@
+drop database walmartdb;
+drop user walmart;
+drop sequence user_seq;
+create user walmart with password 'walmart@123';
+create database walmartdb with template=template0 owner=walmart;
+\connect walmartdb;
+alter default privileges grant all on tables to walmart;
+alter default privileges grant all on sequences to walmart;
+
+create table orgs_enabled(
+ org_id integer primary key not null,
+ org_name varchar(30) not null,
+ is_enabled boolean not null default true
+);
+
+create table users (
+ user_id UUID primary key not null,
+ first_name varchar(20) not null,
+ last_name varchar(20) not null,
+ email varchar(30) not null,
+ password text not null,
+ org_id integer not null
+);
+
+alter table users add constraint org_id_fk foreign key (org_id) references orgs_enabled(org_id);
+
+create sequence user_seq increment 1 start 1;
+create sequence org_id increment 1 start 1;
\ No newline at end of file
diff --git a/src/main/java/com/example/forecasting/ForecastingApplication.java b/src/main/java/com/example/forecasting/ForecastingApplication.java
new file mode 100644
index 0000000..2d7610d
--- /dev/null
+++ b/src/main/java/com/example/forecasting/ForecastingApplication.java
@@ -0,0 +1,13 @@
+package com.example.forecasting;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ForecastingApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ForecastingApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/example/forecasting/datasource/PostgresDataSource.java b/src/main/java/com/example/forecasting/datasource/PostgresDataSource.java
new file mode 100644
index 0000000..b3f9133
--- /dev/null
+++ b/src/main/java/com/example/forecasting/datasource/PostgresDataSource.java
@@ -0,0 +1,43 @@
+package com.example.forecasting.datasource;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.couchbase.CouchbaseProperties;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.jdbc.DataSourceBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+import javax.sql.DataSource;
+import javax.validation.constraints.NotNull;
+
+@Configuration
+public class PostgresDataSource {
+
+ @Autowired
+ private Environment environment;
+
+ @Bean
+ @ConfigurationProperties("app.postgresource")
+ public HikariDataSource hikariDataSource(){
+ System.out.println("My Own Spring Bean of Data Source is Created ");
+ HikariDataSource hds = DataSourceBuilder
+ .create()
+ .type(HikariDataSource.class)
+ .build();
+
+ Integer maxPoolSize = Integer.parseInt(environment.getProperty("app.postgresource.maxPoolSize"));
+ hds.setJdbcUrl(environment.getProperty("app.postgresource.jdbcUrl"));
+ hds.setUsername(environment.getProperty("app.postgresource.username"));
+ hds.setPassword(environment.getProperty("app.postgresource.password"));
+ hds.setMaximumPoolSize(maxPoolSize);
+ return hds;
+ }
+
+ @Bean("walmart-postgres")
+ public JdbcTemplate jdbcTemplate(){
+ return new JdbcTemplate(hikariDataSource());
+ }
+}
diff --git a/src/main/java/com/example/forecasting/datasource/PostgresDataSourceLoader.java b/src/main/java/com/example/forecasting/datasource/PostgresDataSourceLoader.java
new file mode 100644
index 0000000..4435c9b
--- /dev/null
+++ b/src/main/java/com/example/forecasting/datasource/PostgresDataSourceLoader.java
@@ -0,0 +1,26 @@
+package com.example.forecasting.datasource;
+
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+@Component
+public class PostgresDataSourceLoader implements ApplicationRunner {
+ private final HikariDataSource hikariDataSource;
+
+ @Autowired
+ public PostgresDataSourceLoader(HikariDataSource hikariDataSource) {
+ this.hikariDataSource = hikariDataSource;
+ }
+
+ @Override
+ public void run(ApplicationArguments args) throws SQLException {
+ Connection conn = hikariDataSource.getConnection();
+ conn.close();
+ }
+}
diff --git a/src/main/java/com/example/forecasting/entities/Org.java b/src/main/java/com/example/forecasting/entities/Org.java
new file mode 100644
index 0000000..102fca3
--- /dev/null
+++ b/src/main/java/com/example/forecasting/entities/Org.java
@@ -0,0 +1,20 @@
+package com.example.forecasting.entities;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import javax.validation.constraints.NotBlank;
+
+public class Org {
+
+ @NotBlank(message = "org name cannot be blank")
+ private String orgName;
+
+ public Org(@JsonProperty(value = "org_name", required = true) String orgName){
+ this.orgName = orgName;
+ }
+
+ public String getOrgName(){
+ return orgName;
+ }
+}
diff --git a/src/main/java/com/example/forecasting/entities/User.java b/src/main/java/com/example/forecasting/entities/User.java
new file mode 100644
index 0000000..997d88a
--- /dev/null
+++ b/src/main/java/com/example/forecasting/entities/User.java
@@ -0,0 +1,72 @@
+package com.example.forecasting.entities;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.springframework.lang.Nullable;
+import javax.validation.constraints.NotBlank;
+import java.util.UUID;
+
+@JsonIgnoreProperties(value = {"id"})
+public class User {
+
+ private final UUID id;
+ @NotBlank(message = "First Name cannot be blank.")
+ private final String firstName;
+ // TODO implement a custom validator
+ @Nullable
+ private final String lastName;
+ @NotBlank(message = "email cannot be blank.")
+ private final String email;
+ @NotBlank(message = "password cannot be blank.")
+ private final String password;
+ // TODO implement a custom validator
+ private final String orgName;
+
+
+ public User(UUID id,
+ @JsonProperty(value = "first_name", required = true) String firstName,
+ @Nullable @JsonProperty("last_name") String lastName,
+ @JsonProperty(value = "email", required = true) String email,
+ @JsonProperty(value = "password",
+ access = JsonProperty.Access.WRITE_ONLY,
+ required = true) String password,
+ @JsonProperty(value = "org_name", required = true) String orgName)
+ {
+
+ System.out.println("id is " + id);
+ System.out.println("first name is " + firstName);
+ System.out.println("last name is " + lastName);
+ System.out.println("email is " + email);
+ System.out.println("password is " + password);
+ System.out.println("org name is " + orgName);
+
+ this.id = id;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.email = email;
+ this.password = password;
+ this.orgName = orgName;
+ }
+
+ public String getOrgName() {
+ return orgName;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ @Nullable
+ public String getLastName() {
+ return lastName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+}
diff --git a/src/main/java/com/example/forecasting/exceptions/GlobalExceptionHandler.java b/src/main/java/com/example/forecasting/exceptions/GlobalExceptionHandler.java
new file mode 100644
index 0000000..06063b5
--- /dev/null
+++ b/src/main/java/com/example/forecasting/exceptions/GlobalExceptionHandler.java
@@ -0,0 +1,25 @@
+package com.example.forecasting.exceptions;
+
+import com.fasterxml.jackson.databind.exc.MismatchedInputException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.MissingMatrixVariableException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@ControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(MismatchedInputException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ResponseBody
+ public Map reyHandleChey(MismatchedInputException e){
+ Map map = new HashMap<>();
+ map.put("message", "rey handle chestunna message ila vacchindi");
+ return map;
+ }
+}
diff --git a/src/main/java/com/example/forecasting/exceptions/ResourceAlreadyExists.java b/src/main/java/com/example/forecasting/exceptions/ResourceAlreadyExists.java
new file mode 100644
index 0000000..44b9321
--- /dev/null
+++ b/src/main/java/com/example/forecasting/exceptions/ResourceAlreadyExists.java
@@ -0,0 +1,11 @@
+package com.example.forecasting.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.CONFLICT)
+public class ResourceAlreadyExists extends RuntimeException{
+ public ResourceAlreadyExists(String message){
+ super(message);
+ }
+}
diff --git a/src/main/java/com/example/forecasting/exceptions/ResourceNotFoundException.java b/src/main/java/com/example/forecasting/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000..4539c0e
--- /dev/null
+++ b/src/main/java/com/example/forecasting/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,15 @@
+package com.example.forecasting.exceptions;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+@ResponseStatus(HttpStatus.NOT_FOUND)
+public class ResourceNotFoundException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ResourceNotFoundException(String message){
+ super(message);
+ }
+}
+
diff --git a/src/main/java/com/example/forecasting/repositories/OrgRepository.java b/src/main/java/com/example/forecasting/repositories/OrgRepository.java
new file mode 100644
index 0000000..1c2a414
--- /dev/null
+++ b/src/main/java/com/example/forecasting/repositories/OrgRepository.java
@@ -0,0 +1,12 @@
+package com.example.forecasting.repositories;
+
+public interface OrgRepository {
+
+ public void createOrg(String org_name);
+
+ public Integer getOrgId(String org_name);
+
+ public void disableOrg(String org_name);
+
+
+}
diff --git a/src/main/java/com/example/forecasting/repositories/OrgRepositoryImpl.java b/src/main/java/com/example/forecasting/repositories/OrgRepositoryImpl.java
new file mode 100644
index 0000000..5aab4a4
--- /dev/null
+++ b/src/main/java/com/example/forecasting/repositories/OrgRepositoryImpl.java
@@ -0,0 +1,56 @@
+package com.example.forecasting.repositories;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.PreparedStatementCreator;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.stereotype.Repository;
+
+import javax.sql.StatementEvent;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+
+
+@Repository("jdbcOrgTemplateDao")
+public class OrgRepositoryImpl implements OrgRepository {
+
+ @Qualifier("walmart-postgres")
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ private static final String ORG_CREATE_SQL = "insert into orgs_enabled (org_id, org_name) values (nextval('org_id_seq'), ?)";
+
+ private static final String GET_ORG_ID = "select org_id from orgs_enabled where org_name = ?";
+
+ @Override
+ public void createOrg(String org_name){
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+ jdbcTemplate.update(connection -> {
+ PreparedStatement ps = connection.prepareStatement(ORG_CREATE_SQL,
+ Statement.RETURN_GENERATED_KEYS);
+ ps.setString(1, org_name);
+ return ps;
+ }, keyHolder);
+
+ Integer createdOrgId = (Integer) keyHolder.getKeys().get("org_id");
+ }
+
+ @Override
+ public Integer getOrgId(String org_name){
+ return jdbcTemplate.queryForObject(GET_ORG_ID, new Object[]{org_name}, orgIdMapper);
+ }
+
+ private RowMapper orgIdMapper = ((rs, rowNum) -> {
+ return new Integer(rs.getString("org_id"));
+ });
+
+ @Override
+ public void disableOrg(String org_name){
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/forecasting/repositories/UserRepository.java b/src/main/java/com/example/forecasting/repositories/UserRepository.java
new file mode 100644
index 0000000..b1d7e47
--- /dev/null
+++ b/src/main/java/com/example/forecasting/repositories/UserRepository.java
@@ -0,0 +1,19 @@
+package com.example.forecasting.repositories;
+
+
+import com.example.forecasting.entities.User;
+import org.springframework.stereotype.Repository;
+
+import java.util.UUID;
+
+
+@Repository
+public interface UserRepository {
+
+ public void createUser(String firstName, String lastName, String email, String password, Integer orgId);
+
+ public Integer getCountByEmail(String email);
+
+ public User validateUser(String email, String password);
+
+}
diff --git a/src/main/java/com/example/forecasting/repositories/jdbcUserRepositoryImpl.java b/src/main/java/com/example/forecasting/repositories/jdbcUserRepositoryImpl.java
new file mode 100644
index 0000000..0a811d5
--- /dev/null
+++ b/src/main/java/com/example/forecasting/repositories/jdbcUserRepositoryImpl.java
@@ -0,0 +1,44 @@
+package com.example.forecasting.repositories;
+
+import com.example.forecasting.entities.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+
+
+@Repository("jdbcTemplateDao")
+public class jdbcUserRepositoryImpl implements UserRepository {
+
+ @Qualifier("walmart-postgres")
+ @Autowired
+ private JdbcTemplate jdbcTemplate;
+
+ private static final String SQL_CREATE = "insert into users (user_id, first_name, last_name, email, password, org_id) values (uuid_generate_v4(), ?, ?, ?, ?, ?)";
+
+ @Override
+ public void createUser(String firstName, String lastName, String email, String password, Integer orgId) {
+ jdbcTemplate.update(connection -> {
+ PreparedStatement ps = connection.prepareStatement(SQL_CREATE);
+ ps.setString(1, firstName);
+ ps.setString(2, lastName);
+ ps.setString(3, email);
+ ps.setString(4, password);
+ ps.setInt(5, orgId);
+ return ps;
+ });
+ }
+
+ @Override
+ public Integer getCountByEmail(String email) {
+ return null;
+ }
+
+ @Override
+ public User validateUser(String email, String password) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/example/forecasting/resources/OrgResource.java b/src/main/java/com/example/forecasting/resources/OrgResource.java
new file mode 100644
index 0000000..0e3d0ba
--- /dev/null
+++ b/src/main/java/com/example/forecasting/resources/OrgResource.java
@@ -0,0 +1,25 @@
+package com.example.forecasting.resources;
+
+import com.example.forecasting.entities.Org;
+import com.example.forecasting.services.OrgService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+@RestController
+@RequestMapping(value = "/orgs/")
+public class OrgResource {
+
+ @Autowired
+ private OrgService orgService;
+
+ @PostMapping(value = "/register_org")
+ public void registerOrg(@Valid @NotNull @RequestBody Org orgBody) {
+ orgService.createOrg(orgBody.getOrgName());
+ }
+}
diff --git a/src/main/java/com/example/forecasting/resources/UserResource.java b/src/main/java/com/example/forecasting/resources/UserResource.java
new file mode 100644
index 0000000..46c5625
--- /dev/null
+++ b/src/main/java/com/example/forecasting/resources/UserResource.java
@@ -0,0 +1,42 @@
+package com.example.forecasting.resources;
+
+
+import com.example.forecasting.entities.User;
+import com.example.forecasting.services.UserService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/users/")
+public class UserResource {
+
+ private final UserService userService;
+
+ @Autowired
+ public UserResource(UserService userService) {
+ this.userService = userService;
+ }
+
+ @PostMapping("/login")
+ public ResponseEntity