diff --git a/pom.xml b/pom.xml
index 7931107..657ac3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -154,6 +154,25 @@
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.17.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.17.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.1
+
+
\ No newline at end of file
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/MongoStorage.java b/src/main/java/xyz/refinedev/api/storage/mongo/MongoStorage.java
deleted file mode 100644
index 2dd3e5c..0000000
--- a/src/main/java/xyz/refinedev/api/storage/mongo/MongoStorage.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package xyz.refinedev.api.storage.mongo;
-
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.Filters;
-import com.mongodb.client.model.ReplaceOptions;
-
-import com.mongodb.client.model.Updates;
-import org.bson.Document;
-import org.bson.conversions.Bson;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * This Project is property of Refine Development © 2021 - 2022
- * Redistribution of this Project is not allowed
- *
- * @author Drizzy
- * Created: 7/27/2022
- * Project: StorageAPI
- */
-
-@SuppressWarnings("unused")
-public class MongoStorage {
-
- private static final ReplaceOptions REPLACE_OPTIONS = new ReplaceOptions().upsert(true);
-
- private final MongoCollection collection;
- private final Gson gson;
- private final Type typeToken;
-
- public MongoStorage(MongoCollection collection, Gson gson) {
- this.collection = collection;
- this.gson = gson;
- this.typeToken = new TypeToken() {}.getType();
- }
-
- public CompletableFuture> fetchAllEntries() {
- return CompletableFuture.supplyAsync(() -> {
- List found = new ArrayList<>();
- for (Document document : this.collection.find()) {
- if (document == null) {
- continue;
- }
- found.add(this.gson.fromJson(document.toJson(), typeToken));
- }
- return found;
- });
- }
-
- public CompletableFuture> fetchAllRawEntries() {
- return CompletableFuture.supplyAsync(() -> {
- List found = new ArrayList<>();
- for (Document document : this.collection.find()) {
- found.add(document);
- }
- return found;
- });
- }
-
- public void saveData(UUID key, V value, Type type) {
- CompletableFuture.runAsync(() -> this.saveDataSync(key, value, type));
- }
-
- public void saveDataSync(UUID key, V value, Type type) {
- Bson query = Filters.eq("_id", key.toString());
- Document parsed = Document.parse(gson.toJson(value, type));
- this.collection.replaceOne(query, parsed, REPLACE_OPTIONS);
- }
-
- public void saveRawData(UUID key, Document document) {
- CompletableFuture.runAsync(() -> this.saveRawDataSync(key, document));
- }
-
- public void saveRawDataSync(UUID key, Document document) {
- Bson query = Filters.eq("_id", key.toString());
- this.collection.replaceOne(query, document, REPLACE_OPTIONS);
- }
-
- public V loadData(UUID key, Type type) {
- Bson query = Filters.eq("_id", key.toString());
-
- Document document = this.collection.find(query).first();
- if (document == null) return null;
-
- return this.gson.fromJson(document.toJson(), type);
- }
-
- public CompletableFuture loadDataAsync(UUID key, Type type) {
- return CompletableFuture.supplyAsync(() -> this.loadData(key, type));
- }
-
- public Document loadRawData(UUID key) {
- Bson query = Filters.eq("_id", key.toString());
- return this.collection.find(query).first();
- }
-
- public CompletableFuture loadRawDataAsync(UUID key) {
- return CompletableFuture.supplyAsync(() -> this.loadRawData(key));
- }
-
- public void deleteData(UUID key) {
- CompletableFuture.runAsync(() -> {
- Bson query = Filters.eq("_id", key.toString());
- this.collection.deleteOne(query);
- });
- }
-
- /**
- * Delete a certain key in all documents inside the collection
- * Uses long because it could surpass the limit of integer
- *
- * @param key {@link String key}
- * @return {@link Integer amount of deleted documents}
- */
- public CompletableFuture deleteKeyInAll(String key) {
- return CompletableFuture.supplyAsync(() -> {
- // Unset the key
- Bson combinedUpdate = Updates.unset(key);
-
- // Apply the updates to all documents in the collection
- return collection.updateMany(new Document(), combinedUpdate).getModifiedCount(); // new Document() is an empty filter, meaning "all documents";
- });
- }
-}
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/model/Model.java b/src/main/java/xyz/refinedev/api/storage/mongo/model/Model.java
new file mode 100644
index 0000000..13ad9d4
--- /dev/null
+++ b/src/main/java/xyz/refinedev/api/storage/mongo/model/Model.java
@@ -0,0 +1,9 @@
+package xyz.refinedev.api.storage.mongo.model;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public interface Model {
+
+ @JsonProperty("_id")
+ String getId();
+}
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/repository/MongoObjectRepository.java b/src/main/java/xyz/refinedev/api/storage/mongo/repository/MongoObjectRepository.java
new file mode 100644
index 0000000..dc0cbe3
--- /dev/null
+++ b/src/main/java/xyz/refinedev/api/storage/mongo/repository/MongoObjectRepository.java
@@ -0,0 +1,40 @@
+package xyz.refinedev.api.storage.mongo.repository;
+
+import com.google.common.collect.Lists;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+import lombok.RequiredArgsConstructor;
+import xyz.refinedev.api.storage.mongo.model.Model;
+
+import java.util.List;
+
+@RequiredArgsConstructor
+public class MongoObjectRepository implements ObjectRepository {
+
+ private final MongoCollection collection;
+
+ @Override
+ public O find(String id) {
+ return collection.find(Filters.eq("_id", id)).first();
+ }
+
+ @Override
+ public void remove(String id) {
+ collection.deleteOne(Filters.eq("_id", id));
+ }
+
+ @Override
+ public void save(O model) {
+ collection.replaceOne(
+ Filters.eq("_id", model.getId()),
+ model,
+ new ReplaceOptions().upsert(true)
+ );
+ }
+
+ @Override
+ public List findAll() {
+ return collection.find().into(Lists.newArrayList());
+ }
+}
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/repository/ObjectRepository.java b/src/main/java/xyz/refinedev/api/storage/mongo/repository/ObjectRepository.java
new file mode 100644
index 0000000..2fed114
--- /dev/null
+++ b/src/main/java/xyz/refinedev/api/storage/mongo/repository/ObjectRepository.java
@@ -0,0 +1,12 @@
+package xyz.refinedev.api.storage.mongo.repository;
+
+import xyz.refinedev.api.storage.mongo.model.Model;
+
+import java.util.List;
+
+public interface ObjectRepository {
+ O find(String id);
+ void remove(String id);
+ void save(O model);
+ List findAll();
+}
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorage.java b/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorage.java
new file mode 100644
index 0000000..29fa3f8
--- /dev/null
+++ b/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorage.java
@@ -0,0 +1,124 @@
+package xyz.refinedev.api.storage.mongo.storage;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.ReplaceOptions;
+
+import com.mongodb.client.model.Updates;
+import lombok.RequiredArgsConstructor;
+import org.bson.Document;
+import org.bson.conversions.Bson;
+import xyz.refinedev.api.storage.mongo.model.Model;
+import xyz.refinedev.api.storage.mongo.repository.MongoObjectRepository;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * This Project is property of Refine Development © 2021 - 2022
+ * Redistribution of this Project is not allowed
+ *
+ * @author Drizzy
+ * Created: 7/27/2022
+ * Project: StorageAPI
+ *
+ * Legacy implementation using {@link Document} + Gson.
+ *
+ * @deprecated Use {@link MongoStorageManager} with typed repositories instead.
+ */
+
+@Deprecated
+@SuppressWarnings("unused")
+@RequiredArgsConstructor
+public class MongoStorage {
+
+ private static final ReplaceOptions REPLACE_OPTIONS = new ReplaceOptions().upsert(true);
+
+ private final MongoCollection collection;
+ private final Gson gson;
+ private final Type typeToken = new TypeToken() {}.getType();
+
+ public MongoObjectRepository asRepository(MongoCollection typedCollection) {
+ return new MongoObjectRepository<>(typedCollection);
+ }
+
+ public CompletableFuture> fetchAllEntries() {
+ return CompletableFuture.supplyAsync(() -> {
+ List found = new ArrayList<>();
+ for (Document document : this.collection.find()) {
+ if (document == null) continue;
+ found.add(this.gson.fromJson(document.toJson(), typeToken));
+ }
+ return found;
+ });
+ }
+
+ public CompletableFuture> fetchAllRawEntries() {
+ return CompletableFuture.supplyAsync(() -> {
+ List found = new ArrayList<>();
+ for (Document document : this.collection.find()) {
+ found.add(document);
+ }
+ return found;
+ });
+ }
+
+ public void saveData(UUID key, V value, Type type) {
+ CompletableFuture.runAsync(() -> this.saveDataSync(key, value, type));
+ }
+
+ public void saveDataSync(UUID key, V value, Type type) {
+ Bson query = Filters.eq("_id", key.toString());
+ Document parsed = Document.parse(gson.toJson(value, type));
+ this.collection.replaceOne(query, parsed, REPLACE_OPTIONS);
+ }
+
+ public void saveRawData(UUID key, Document document) {
+ CompletableFuture.runAsync(() -> this.saveRawDataSync(key, document));
+ }
+
+ public void saveRawDataSync(UUID key, Document document) {
+ Bson query = Filters.eq("_id", key.toString());
+ this.collection.replaceOne(query, document, REPLACE_OPTIONS);
+ }
+
+ public V loadData(UUID key, Type type) {
+ Bson query = Filters.eq("_id", key.toString());
+ Document document = this.collection.find(query).first();
+ if (document == null) return null;
+ return this.gson.fromJson(document.toJson(), type);
+ }
+
+ public CompletableFuture loadDataAsync(UUID key, Type type) {
+ return CompletableFuture.supplyAsync(() -> this.loadData(key, type));
+ }
+
+ public Document loadRawData(UUID key) {
+ Bson query = Filters.eq("_id", key.toString());
+ return this.collection.find(query).first();
+ }
+
+ public CompletableFuture loadRawDataAsync(UUID key) {
+ return CompletableFuture.supplyAsync(() -> this.loadRawData(key));
+ }
+
+ public void deleteData(UUID key) {
+ CompletableFuture.runAsync(() -> {
+ Bson query = Filters.eq("_id", key.toString());
+ this.collection.deleteOne(query);
+ });
+ }
+
+ public CompletableFuture deleteKeyInAll(String key) {
+ return CompletableFuture.supplyAsync(() -> {
+ Bson combinedUpdate = Updates.unset(key);
+ return collection.updateMany(new Document(), combinedUpdate).getModifiedCount();
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorageManager.java b/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorageManager.java
new file mode 100644
index 0000000..d126d54
--- /dev/null
+++ b/src/main/java/xyz/refinedev/api/storage/mongo/storage/MongoStorageManager.java
@@ -0,0 +1,49 @@
+package xyz.refinedev.api.storage.mongo.storage;
+
+import com.mongodb.ConnectionString;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.client.MongoClient;
+import com.mongodb.client.MongoClients;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoDatabase;
+import lombok.Getter;
+import xyz.refinedev.api.storage.mongo.model.Model;
+import xyz.refinedev.api.storage.mongo.repository.MongoObjectRepository;
+import xyz.refinedev.api.storage.mongo.repository.ObjectRepository;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.bson.codecs.pojo.PojoCodecProvider;
+
+import static org.bson.codecs.configuration.CodecRegistries.*;
+
+@Getter
+public class MongoStorageManager {
+
+ private final MongoClient client;
+ private final MongoDatabase database;
+
+ public MongoStorageManager(String connectionUri, String dbName) {
+ ConnectionString connString = new ConnectionString(connectionUri);
+
+ CodecRegistry pojoCodecRegistry = fromRegistries(
+ MongoClientSettings.getDefaultCodecRegistry(),
+ fromProviders(PojoCodecProvider.builder().automatic(true).build())
+ );
+
+ MongoClientSettings settings = MongoClientSettings.builder()
+ .applyConnectionString(connString)
+ .codecRegistry(pojoCodecRegistry)
+ .build();
+
+ this.client = MongoClients.create(settings);
+ this.database = client.getDatabase(dbName);
+ }
+
+ public ObjectRepository getRepository(Class clazz, String collectionName) {
+ MongoCollection collection = database.getCollection(collectionName, clazz);
+ return new MongoObjectRepository<>(collection);
+ }
+
+ public void close() {
+ client.close();
+ }
+}
diff --git a/src/test/java/dev/joordih/api/storage/Storage.java b/src/test/java/dev/joordih/api/storage/Storage.java
new file mode 100644
index 0000000..fec3a02
--- /dev/null
+++ b/src/test/java/dev/joordih/api/storage/Storage.java
@@ -0,0 +1,34 @@
+package dev.joordih.api.storage;
+
+import dev.joordih.api.storage.user.User;
+import dev.joordih.api.storage.user.UserManager;
+import xyz.refinedev.api.storage.mongo.storage.MongoStorageManager;
+
+import java.util.UUID;
+
+public class Storage {
+
+ public static void main(String[] args) {
+ MongoStorageManager storage = new MongoStorageManager(
+ "mongodb://localhost:27017",
+ "storage-api"
+ );
+
+ UserManager userManager = new UserManager(storage);
+
+ UUID userUuid = UUID.randomUUID();
+ User user = User.builder()
+ .id(userUuid.toString())
+ .uuid(userUuid)
+ .points(100)
+ .level(1)
+ .build();
+
+ userManager.getRepository().save(user);
+
+ User loadedUser = userManager.getRepository().find(userUuid.toString());
+ System.out.println(loadedUser);
+
+ storage.close();
+ }
+}
diff --git a/src/test/java/dev/joordih/api/storage/user/User.java b/src/test/java/dev/joordih/api/storage/user/User.java
new file mode 100644
index 0000000..14c2332
--- /dev/null
+++ b/src/test/java/dev/joordih/api/storage/user/User.java
@@ -0,0 +1,28 @@
+package dev.joordih.api.storage.user;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.*;
+import xyz.refinedev.api.storage.mongo.model.Model;
+
+import java.util.UUID;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class User implements Model {
+
+ @JsonProperty("_id")
+ private String id;
+
+ private UUID uuid;
+ private int points;
+ private int level;
+
+ @JsonIgnore
+ public boolean isPremium() {
+ return points >= 1000;
+ }
+}
diff --git a/src/test/java/dev/joordih/api/storage/user/UserManager.java b/src/test/java/dev/joordih/api/storage/user/UserManager.java
new file mode 100644
index 0000000..efba6e8
--- /dev/null
+++ b/src/test/java/dev/joordih/api/storage/user/UserManager.java
@@ -0,0 +1,15 @@
+package dev.joordih.api.storage.user;
+
+import lombok.Getter;
+import xyz.refinedev.api.storage.mongo.repository.ObjectRepository;
+import xyz.refinedev.api.storage.mongo.storage.MongoStorageManager;
+
+@Getter
+public class UserManager {
+
+ private final ObjectRepository repository;
+
+ public UserManager(MongoStorageManager storageManager) {
+ this.repository = storageManager.getRepository(User.class, "users");
+ }
+}