Key value store (KVS)
Introduction
KVS stands for Key Value Store and it’s a way to store data following a key-value structure
Structure of a KVS
For the different projects, to standardize the KVS, the IDs of fields (setted with pfqcn) will be used as keys of KVS to become more precise, facilitate and standardize the KVS structures.
- key : id of the field + . + action
- Example: pfqcn_phpros_posBundle_informationForm_client_input.show
{
"id": 3,
"key": "pfqcn_phprpos_posBundle_demo_informationForm_deliveryDate_field.show",
"value": "1",
"description": "Hide/Show the delivery date input (0) to hide (1) to show"
"shortcode": "KVS",
"user": {
"id": 1,
"shortcode": "UU",
"toString": "admin UU1",
"uniqueId": "UU1",
},
"corporation": null,
"clearanceLevel": 7,
"slug": "38401502-a075-11ee-b979-067e394b3036",
"fieldFromKey": "show",
"uniqueId": "KVS3",
"enabled": true,
"disabled": false
}How to use KVS in your app
In layout
Import the KVSProvider from the contexts
// If you are using utils bundle
import { KVSProvider } from "@phpcreation/frontend-utils-react-nextjs-bundle/contexts";
// If you copied the context in your app
import { KVSProvider } from "@/contexts/KVSContext";Englobe your app with the KVSProvider
<KVSProvider>{children}</KVSProvider>In pages where you need to use KVS
Import the useKVS hook from the contexts
// If you are using utils bundle
import { useKVS } from "@phpcreation/frontend-utils-react-nextjs-bundle/contexts";
// If you copied the context in your app
import { useKVS } from "@/contexts/KVSContext";Get the function to get specific KVS from the context
const { getSpecificKVS } = useKVS();This function will return a promise with a KVS (or null if the KVS doesn’t exist)
Use the function to get the KVS you need
const descriptionOptionalKVS = await getSpecificKVS(
"phpreaction.project.task_manager.description_minimum"
);How is KVSContext implemented
Variables
interface KVSList {
[key: string]: KVS;
}
const user = useUser(); // You can adapt this to your way to get the user
const tenant = useTenant(); // You can adapt this to your way to get the tenant
const keyCache = `${tenant}_${user?.id}_KVS`;
const [userKVS, setUserKVS] = useState<KVSList>({});- user : Will be used to get the user id (User id will be used for caching keys)
- tenant : To get the tenant for API call and for caching keys
- keyCache : Will be used as the key to save in localStorage and cache
- userKVS : Will be the list of user’s KVS
Functions
getUserKVS
This will be used at the beginning in a useEffect to get the list of KVS of the user
- It’ll get it in this order (If it doesn’t exist in the step, it’ll go to the next one) :
- From userKVS state
- From localStorage
- From cache
- From API
- Then updateKVSList, this will set the KVS :
- In userKVS state
- In localStorage
- In cache
const getUserKVS = async (): Promise<KVSList> => {
try {
let kvs = userKVS;
if (Object.keys(kvs).length > 0) return kvs;
kvs = getKVSLocalStorage();
if (Object.keys(kvs).length > 0) {
await updateMultipleKVS(kvs);
return kvs;
}
kvs = await getKVSCache();
if (Object.keys(kvs).length > 0) {
await updateMultipleKVS(kvs);
return kvs;
}
const { data } = await CallAPI(
"GET",
tenant,
CallAPIURL.keyvaluestore_keyvalues.get + `?user=${user?.id}`
);
if (data.response["hydra:totalItems"] === 0) return {};
const userKVSList = data.response["hydra:member"].reduce(
(acc: KVSList, kvs: KVS) => {
if (kvs) acc[kvs.key] = kvs;
return acc;
},
{}
);
await updateMultipleKVS(userKVSList);
return userKVSList;
} catch (error) {
console.error(error);
return {};
}
};getKVSCache
This function will get the list of user’s kvs with the keyCache
const getKVSCache = async () => {
const kvsList = await getCachedData(keyCache);
setUserKVS(kvsList);
return kvsList;
};updateSingleKVS
This function will update with a single KVS: It’ll merge it with the userKVS object then update(updateMultipleKVS) everything
const updateSingleKVS = async (kvs: KVS) => {
if (kvs) {
const kvsList = { ...userKVS, [kvs?.key]: kvs };
await updateMultipleKVS(kvsList);
}
};updateMultipleKVS
This function will update with multiple KVS
const updateMultipleKVS = async (kvsList: KVSList) => {
setUserKVS(kvsList);
localStorage.setItem(keyCache, JSON.stringify(kvsList));
await storeToCache(keyCache, JSON.stringify(kvsList));
};getSpecificKVS
This function is the important one, because it’ll be used everywhere.
- It’ll first get the searched KVS in the userKVS object if it’s inside
- If not, it’ll then look for that KVS in cache
- If not, it’ll get it from API then save it in the object and in cache
const getSpecificKVS = async (key: string): Promise<KVS> => {
try {
// Get in the array
let kvs = userKVS[key];
if (kvs) return kvs;
// Get in the cache
kvs = await getKVSCache(key);
if (kvs) return kvs;
const { data } = await CallAPI(
"GET",
tenant,
`${CallAPIURL.keyvaluestore_keyvalues.get}?key=${key}`
);
kvs = data.response["hydra:member"][0];
if (kvs) {
setUserKVS((prev) => ({ ...prev, [kvs.key]: kvs }));
await saveKVSCache(kvs);
}
return kvs;
} catch (error) {
console.error(error);
return null;
}
};createKVS
Function to create a KVS
const createKVS = async (
key: string,
value: string,
clearanceLevel: number = 7
) => {
try {
const { data } = await CallAPI(
"POST",
tenant,
CallAPIURL.keyvaluestore_keyvalues.get,
"",
JSON.stringify({
key,
value,
user: API_ENDPOINT_VERSION + "/users/" + user?.id,
clearanceLevel,
})
);
const kvs = data.response["hydra:member"];
if (kvs) {
setUserKVS((prev) => ({ ...prev, [kvs.key]: kvs }));
await saveKVSCache(kvs);
}
return data;
} catch (err) {
console.error(err);
}
};Last updated on