Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 23 commits into
base: feedback
Choose a base branch
from
Open
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
34 changes: 34 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/
credentials.json
19 changes: 19 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
115 changes: 115 additions & 0 deletions App Script
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@

function extractSheetId(url) {
// Regular expression to match the sheet ID in the Google Sheets URL
var regex = /\/d\/(.*?)\/edit/;
var match = url.match(regex);

if (match && match[1]) {
return match[1]; // Return the sheet ID
} else {
throw new Error("Sheet ID not found in URL.");
}
}

function sendNotification(e) {
var url =SpreadsheetApp.getActiveSpreadsheet().getUrl();;
try {
var sheetId = extractSheetId(url);
console.log("Sheet ID:", sheetId);
} catch (error) {
console.error(error.message);
}
console.log("sendNotification function called");
if (!e) {
console.error("Event object is undefined");
return null;
}
if (!e.source) {
console.error("Event object is missing 'source' property");
return null;
}
if (!e.range) {
console.error("Event object is missing 'range' property");
return null;
}

try {
var sheet = e.source.getActiveSheet();
if (!sheet) {
console.error("Unable to get active sheet");
return null;
}

var range = e.range;
console.log("Range:", range.getA1Notation());

var newValue, oldValue, columnName, id, timestamp;
try {
newValue = range.getValue();
console.log("New value:", newValue);
} catch (error) {
console.error("Error getting new value:", error.toString());
}

try {
oldValue = e.oldValue || null;
console.log("Old value:", oldValue);
} catch (error) {
console.error("Error getting old value:", error.toString());
}

try {
var column = range.getColumn();
columnName = sheet.getRange(1, column).getValue();
console.log("Top cell value in column:", columnName);
} catch (error) {
console.error("Error getting top cell value:", error.toString());
}

try {
id = sheet.getRange(range.getRow(), 1).getValue();
console.log("Value at Column A:", id);
} catch (error) {
console.error("Error getting column A value:", error.toString());
}

timestamp = new Date().toISOString();
console.log("Timestamp:", timestamp);

// var sheetId = sheet.getSheetId();
var payload = {
sheetid: sheetId,
columnName: columnName,
oldValue: oldValue,
newValue: newValue,
id: id,
timestamp: timestamp
};

console.log("Payload prepared:", JSON.stringify(payload));

var url = '<enter your ngrok url>/notifications';

var options = {
'method': 'POST',
'contentType': 'application/json',
'payload': JSON.stringify(payload)
};

try {
var response = UrlFetchApp.fetch(url, options);
console.log("Response status:", response.getResponseCode());
console.log("Response content:", response.getContentText());
} catch (error) {
console.error("Error sending request:", error.toString());
}

console.log("POST request sent to:", url);

return sheetId; // Return the sheet ID

} catch (error) {
console.error("Error in sendNotification:", error.toString());
return null;
}
}
110 changes: 104 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/AHFn7Vbn)
# Superjoin Hiring Assignment

### Welcome to Superjoin's hiring assignment! 🚀
Expand Down Expand Up @@ -44,11 +45,11 @@ Once you're done, make sure you **record a video** showing your project working.

We have a checklist at the bottom of this README file, which you should update as your progress with your assignment. It will help us evaluate your project.

- [ ] My code's working just fine! 🥳
- [ ] I have recorded a video showing it working and embedded it in the README ▶️
- [ ] I have tested all the normal working cases 😎
- [ ] I have even solved some edge cases (brownie points) 💪
- [ ] I added my very planned-out approach to the problem at the end of this README 📜
- [x] My code's working just fine! 🥳
- [x] I have recorded a video showing it working and embedded it in the README ▶️
- [x] I have tested all the normal working cases 😎
- [x] I have even solved some edge cases (brownie points) 💪
- [x] I added my very planned-out approach to the problem at the end of this README 📜

## Got Questions❓
Feel free to check the discussions tab, you might get some help there. Check out that tab before reaching out to us. Also, did you know, the internet is a great place to explore? 😛
Expand All @@ -58,4 +59,101 @@ We're available at [email protected] for all queries.
All the best ✨.

## Developer's Section
*Add your video here, and your approach to the problem (optional). Leave some comments for us here if you want, we will be reading this :)*
### Architectue of the application:
My application is built using Spring Boot and PostgreSQL, providing real-time synchronization **between multiple pairs of database tables and Google Sheets**. The system architecture includes the following key components:

* **Real-Time Synchronization:**

* *Google Sheets Integration*: I use Google Apps Script to send notifications to my backend whenever changes are made in the Google Sheets. This ensures immediate updates and synchronization with the database.

* _Database Integration_: PostgreSQL functions and triggers are employed to notify the backend of any changes detected in the local database. This setup guarantees that updates in the database are promptly reflected in Google Sheets.

**Dynamic Configuration:**

* _Schema Management_: The application dynamically constructs schemas for both the database tables and corresponding Google Sheets. This automatic configuration simplifies the setup process and ensures consistency between data sources.

* _CRUD Operations_: My system supports full Create, Read, Update, and Delete (CRUD) operations for both Google Sheets and the PostgreSQL database. It seamlessly handles modifications and maintains synchronization across platforms.

* _Trigger and Function Management_: The application dynamically creates necessary triggers and functions for each table, facilitating automated responses to data changes.

**Deployment:**

* _Local Database:_ The PostgreSQL database is currently hosted locally.

* _Remote Access_: The Spring Boot application is exposed to the internet using ngrok, allowing external access and interaction with the application.

**Edge Cases:**
* As mentioned above, my application can handle synchronisation _between multiple pairs of database tables and Google Sheets_.
* The linking between tables in the database and Gooogle Sheets is persistent, i.e, the previous link between a Sheet and a Table remains connected even if the application has resatarted, while also being ready to create new links between another table and sheet on user demand.

### Requirements for the Setup:

* **Java Development Kit (JDK):**

- Version 11 or newer, as Spring Boot typically supports recent LTS versions.

* **PostgreSQL**

* **ngrok**
- once ngrok has been setup run the following command:
```
ngrok http 8080
```
### Application-Setup:
* Visit google marketplace > search for sheets > click on the google sheets api and click enable > click on credentails and add a service account > click on the service account and select keys > create a new key, download it and put the JSON file into the directory right outside the src directory in the project.
* in the application.properties file enter your postgres database name in the url and your postgres username and password.
* go to your postgres database and enter the following commands
- ```
CREATE TABLE public.schema_link (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
link VARCHAR(255),
table_name VARCHAR(255)
);
```
- ```
CREATE TABLE public.schema_registry (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
table_name VARCHAR(255),
column_name VARCHAR(255),
column_type VARCHAR(255),
gsheetcol VARCHAR(10)
);
```

### User-Setup:
* The user has to make a Google Sheet and change access from "Restricted" to "Anyone with the link" and set permissions to "Editor"
* When the user runs the application, they will be prompted with a question if they want to create a new table. If they do want to then they must type yes, else the application will still be running just listening to notifiations.
* When they type yes, they will be asked to enter the sheet id. Then they must enter the google sheet's url
* Then they will be prompted to enter the table name, primary key and column names of the table.
* once the table is created on both the google sheets and the database, copy the App Script given in this repository.
* In the google sheets window click on Extentions > App Script.
* Then paste the Appscrpt there.
* then click on the clock like button on the left
* add a new trigger
- set the function to "sendnotification" and "notify me immediately"
- save the trigger
* now the user can use his google sheets and database and synchronisation will start.

### Future Scope:

To enhance the scalability and efficiency of the application, several improvements are planned:

* **Apache Kafka Integration:**

- _High-Frequency Requests_: Implementing Kafka will enable the application to handle high-frequency requests by storing all requests in Kafka topics. This approach will help prevent the loss of requests and ensure that no data is missed.
- _Topic Partitioning:_ By partitioning Kafka topics, the application will benefit from faster bulk data management, as partitions allow for parallel processing and better performance.
* **Conflict Management:**

- _Semaphores vs. Locks_: For managing conflicts when multiple users edit the same table, the application will use semaphores or locks. Given the design of the application, I would prefer semaphores for their simpler implementation.

* **Global Scaling:**

- _YugaByte Database:_ To ensure global scalability, the application will leverage YugaByte’s database solution. The paid version of YugaByte supports global data replication, enabling seamless data distribution and high availability across different geographical locations.


These enhancements will provide robust handling of high-frequency data requests, efficient conflict management, and scalable data distribution, preparing the application for future growth and expanded use cases.


### Video:
[**Watch the video**](https://drive.google.com/file/d/1ItHfXC3zhYEcZADYyoaXU_pSkeHIPba5/view?usp=sharing)

Loading