Skip to content

Commit

Permalink
Custom Storage Option
Browse files Browse the repository at this point in the history
  • Loading branch information
Ali Rıza Kat committed Oct 3, 2024
1 parent ae89395 commit c475d77
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
- Added a new init time config option (conf.storage_type) which can make user set among these storage options:
- File Storage
- Memory Only Storage
- Custom Storage Methods
- Added a new init time config option (conf.custom_storage_method) which enables user to provide custom storage methods.

## 22.06.0
- Fixed a bug where remote config requests were rejected
Expand Down
13 changes: 12 additions & 1 deletion lib/countly-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,22 @@ const fileStorage = {
* @param {StorageTypes} storageType - Whether to use memory only storage or not
* @param {Boolean} isBulk - Whether the storage is for bulk data
* @param {Boolean} persistQueue - Whether to persist the queue until processed
* @param {varies} customStorageMethod - Storage methods provided by the user
*/
var initStorage = function(userPath, storageType, isBulk = false, persistQueue = false) {
var initStorage = function(userPath, storageType, isBulk = false, persistQueue = false, customStorageMethod = null) {
if (storageType === StorageTypes.MEMORY) {
storageMethod = memoryStorage;
}
else if (storageType === StorageTypes.CUSTOM) {
if (customStorageMethod) {
storageMethod = customStorageMethod;
}
else {
cc.log(cc.logLevelEnums.WARNING, `Provided Custom Storage Methods are not valid. Switching to default file storage!`);
storageMethod = fileStorage;
setStoragePath(userPath, isBulk, persistQueue);
}
}
else {
storageMethod = fileStorage;
setStoragePath(userPath, isBulk, persistQueue);
Expand Down
3 changes: 2 additions & 1 deletion lib/countly.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ Countly.Bulk = Bulk;
* @param {string} conf.metrics._locale - locale or language of the device in ISO format
* @param {string} conf.metrics._store - source from where the user/device/installation came from
* @param {StorageTypes} conf.storage_type - to determine which storage type is going to be applied
* @param {varies} conf.custom_storage_method - user given storage methods
* @example
* Countly.init({
* app_key: "{YOUR-APP-KEY}",
Expand Down Expand Up @@ -172,7 +173,7 @@ Countly.Bulk = Bulk;
// Common module debug value is set to init time debug value
cc.debug = conf.debug;

CountlyStorage.initStorage(conf.storage_path, conf.storage_type);
CountlyStorage.initStorage(conf.storage_path, conf.storage_type, false, false, conf.custom_storage_method);

// clear stored device ID if flag is set
if (conf.clear_stored_device_id) {
Expand Down
161 changes: 160 additions & 1 deletion test/tests_storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,53 @@ function recordValuesToStorageAndValidate(userPath, memoryOnly = false, isBulk =
assert.equal(storage.storeGet("cly_object"), undefined);
assert.equal(storage.storeGet("cly_null"), undefined);
}
const funkyMemoryStorage = {
_storage: {},

storeSet: function(key, value, callback) {
if (key) {
const existingValue = this._storage[key];
if (typeof value === 'string' && typeof existingValue === 'string') {
this._storage[key] = existingValue + value;
}
else {
this._storage[key] = value;
}
if (typeof callback === "function") {
callback(null);
}
}
},
storeGet: function(key, def) {
const value = this._storage[key];
if (typeof value === 'string') {
return value.split('').reverse().join('');
}

return value !== undefined ? value : def;
},
storeRemove: function(key) {
delete this._storage[key];
},
};

const customMemoryStorage = {
_storage: {},
storeSet: function(key, value, callback) {
if (key) {
this._storage[key] = value;
if (typeof callback === "function") {
callback(null);
}
}
},
storeGet: function(key, def) {
return typeof this._storage[key] !== "undefined" ? this._storage[key] : def;
},
storeRemove: function(key) {
delete this._storage[key];
},
};

describe("Storage Tests", () => {
it("1- Store Generated Device ID", (done) => {
Expand Down Expand Up @@ -225,6 +272,8 @@ describe("Storage Tests", () => {
done();
});

// resets the storage path to default and validates that it is set correctly,
// then resets it to undefined and confirms the reset.
it("9- Reset Storage While on Default Path /no-init", (done) => {
// will set to default storage path
storage.setStoragePath();
Expand All @@ -235,16 +284,19 @@ describe("Storage Tests", () => {
done();
});

// sets the storage path to default and verifies it,
// then records values to storage and ensures they are stored correctly.
it("10- Recording to Storage with Default Storage Path /no-init", (done) => {
storage.resetStorage();

// Set to default storage path
storage.setStoragePath();
assert.equal(storage.getStoragePath(), "../data/");
recordValuesToStorageAndValidate();
done();
});

// sets a custom storage path and verifies it,
// then records values to storage and ensures correct storage in the custom path.
it("11- Recording to Storage with Custom Storage Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
Expand All @@ -254,6 +306,8 @@ describe("Storage Tests", () => {
done();
});

// sets the storage path to the default bulk storage path and verifies it,
// then records values to bulk storage and validates proper storage in bulk mode.
it("12- Recording to Bulk Storage with Default Bulk Data Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
Expand All @@ -264,6 +318,8 @@ describe("Storage Tests", () => {
done();
});

// sets a custom bulk storage path and verifies it,
// then records values to bulk storage and ensures proper recording to the custom path.
it("13- Recording to Bulk Storage with Custom Bulk Storage Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
Expand Down Expand Up @@ -301,6 +357,9 @@ describe("Storage Tests", () => {
done();
});

// recording device-id in memory only mode
// initializes the SDK in memory only mode, validates that file storage files does not exist
// retrieve the developer supplied device id and id type from storage
it("18- Memory only storage Device-Id", (done) => {
hp.clearStorage();
Countly.init({
Expand All @@ -319,6 +378,9 @@ describe("Storage Tests", () => {
done();
});

// recording event in memory only mode
// initializes the SDK in memory only mode, validates that file storage files does not exist
// records an event and validates the recorded event
it("19- Record event in memory only mode and validate the record", (done) => {
hp.clearStorage();
Countly.init({
Expand All @@ -341,6 +403,9 @@ describe("Storage Tests", () => {
}, hp.mWait);
});

// recording user details in memory only mode
// initializes the SDK in memory only mode, validates that file storage files does not exist
// records user details and validates the recorded details
it("20- Record and validate user details in memory only mode", (done) => {
hp.clearStorage();
Countly.init({
Expand All @@ -360,6 +425,9 @@ describe("Storage Tests", () => {
done();
});

// tests device id changes in memory only storage
// initialize the SDK in memory only mode, check the device id and switch it
// SDK and storage should function properly
it("21- Memory only storage, change SDK Generated Device-Id", (done) => {
hp.clearStorage();
Countly.init({
Expand All @@ -381,6 +449,9 @@ describe("Storage Tests", () => {
done();
});

// tests switching between storage types after initializing SDK
// passing memory storage type during init and initializing storage afterwards
// SDK should switch to file storage
it("22- Switch to file storage after init", (done) => {
hp.clearStorage();
Countly.init({
Expand All @@ -401,6 +472,9 @@ describe("Storage Tests", () => {
done();
});

// tests storeRemove function in CountlyStorage
// after initializing the memory storage, without initializing SDK, attempts to set, get and remove values
// without initializing SDK storage should function properly
it("23- storeRemove Memory Only /no-init", (done) => {
hp.clearStorage();
storage.initStorage(null, StorageTypes.MEMORY);
Expand All @@ -414,6 +488,9 @@ describe("Storage Tests", () => {
done();
});

// tests storeRemove function in CountlyStorage
// after initializing the file storage, without initializing SDK attempts to set, get and remove values
// without initializing SDK storage should function properly
it("24- storeRemove File Storage /no-init", (done) => {
hp.clearStorage();
storage.initStorage();
Expand All @@ -426,4 +503,86 @@ describe("Storage Tests", () => {
assert.equal(storage.storeGet("keyToStore", null), null);
done();
});

// tests init time storage config options
// choosing Custom storage type and passing null in storage methods
// passing null as storage method ends up with switching to default file storage
it("25- Null Custom Storage Method", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
device_id: "Test-Device-Id",
clear_stored_device_id: true,
storage_type: StorageTypes.CUSTOM,
custom_storage_method: null,
});
assert.equal(storage.getStoragePath(), "../data/");
assert.equal(storage.getStorageType(), StorageTypes.FILE);
done();
});

// tests init time storage config options
// choosing Custom storage type and passing custom storage methods
// SDK should use custom methods as storage method, no File Storage should exist
it("26- Providing Custom Storage Method", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
device_id: "Test-Device-Id",
clear_stored_device_id: true,
storage_type: StorageTypes.CUSTOM,
custom_storage_method: customMemoryStorage,
});
hp.doesFileStoragePathsExist((exists) => {
assert.equal(false, exists);
});
done();
});

// tests init time storage config options
// Recording values in Custom Storage Methods
// SDK should use custom methods as storage methods and values should be recorded correctly
it("27- Record/Remove Values in Custom Storage Method", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
device_id: "Test-Device-Id",
clear_stored_device_id: true,
storage_type: StorageTypes.CUSTOM,
custom_storage_method: customMemoryStorage,
});
hp.doesFileStoragePathsExist((exists) => {
assert.equal(false, exists);
});
storage.storeSet("CustomStorageKey", "CustomStorageValue");
assert.equal(storage.storeGet("CustomStorageKey", null), "CustomStorageValue");
storage.storeRemove("CustomStorageKey");
assert.equal(storage.storeGet("CustomStorageKey", null), null);
done();
});

// tests init time storage config options
// passes a funky storage method, which does store get as reversing string
// SDK should use custom methods as storage method
it("28- Record/Remove Values in Custom Storage Method", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
device_id: "Test-Device-Id",
clear_stored_device_id: true,
storage_type: StorageTypes.CUSTOM,
custom_storage_method: funkyMemoryStorage,
});
hp.doesFileStoragePathsExist((exists) => {
assert.equal(false, exists);
});
storage.storeSet("CustomStorageKey", "CustomStorageValue");
storage.storeSet("CustomStorageKey", "CustomStorageValue2");
assert.equal("2eulaVegarotSmotsuCeulaVegarotSmotsuC", storage.storeGet("CustomStorageKey", null));
done();
});
});

0 comments on commit c475d77

Please sign in to comment.