spot-sdk-js
Version:
Develop applications and payloads for Spot using the unofficial Boston Dynamics Spot Node.js SDK.
185 lines (159 loc) • 3.58 kB
JavaScript
const {join} = require('path');
const {homedir, constant} = require('os');
const tmp = require('tmp');
const {readFileSync, unlinkSync, readdirSync, writeFileSync, existsSync, mkdirSync, chmodSync} = require('fs');
class TokenCacheError extends Error {
constructor(msg){
super(msg);
this.name = 'TokenCacheError';
}
};
class ClearFailedError extends TokenCacheError {
constructor(msg){
super(msg);
this.name = 'ClearFailedError';
}
};
class NotInCacheError extends TokenCacheError {
constructor(msg){
super(msg);
this.name = 'NotInCacheError';
}
};
class WriteFailedError extends TokenCacheError {
constructor(msg){
super(msg);
this.name = 'WriteFailedError';
}
};
function atomicFileWrite(data, filename, permissions=0o600){
const temp = tmp.fileSync({ mode: permissions });
writeFileSync(temp.name, data);
const original_umask = process.umask(0);
try{
const directory = path.dirname(filename);
if(!existsSync(directory)){
mkdirSync(directory, {mode: 0o700});
}
}catch(e){
console.error(e);
}
process.umask(original_umask);
try{
copyFileSync(temp.name, filename);
}catch(e){
if(e.errno != constant.errno.EEXIST){
throw e;
}
unlinkSync(filename);
copyFileSync(temp.name, filename);
}
temp.removeCallback();
chmodSync(filename, permissions);
}
/**
* No-op default cache that serves as an interface.
* @class TokenCache
*/
class TokenCache {
constructor(){
};
read(name){
throw new NotInCacheError();
};
clear(name){
return;
};
write(name, token){
return;
};
match(name){
return [];
};
};
/**
* Handles transfer from in memory tokens to arbitrary storage e.g. filesystem.
* @class TokenCacheFilesystem
*/
class TokenCacheFilesystem {
/**
* @param {string} [cache_directory]
*/
constructor(cache_directory = `${homedir()}/.bosdyn/user_tokens`){
this.directory = join(cache_directory);
}
/**
* @param {string} name
* @return {string|Buffer}
* @throws {NotInCacheError}
*/
read(name){
const filename = this._name_to_filename(name);
try {
const data = readFileSync(filename, 'utf8');
return data;
}catch(err){
throw new NotInCacheError(err);
}
}
/**
* @param {string} name
* @return {void}
* @throws {ClearFailedError}
*/
clear(name){
const filename = this._name_to_filename(name);
try{
unlinkSync(filename);
}catch(err){
throw new ClearFailedError(err);
}
}
/**
* @param {string} name
* @param {string} token
* @return {void}
* @throws {WriteFailedError}
*/
write(name, token){
const filename = this._name_to_filename(name);
try{
atomicFileWrite(token, filename);
}catch(err){
throw new WriteFailedError(err);
}
}
/**
* @param {string} name
* @return {Array<string>|array}
*/
match(name){
const matching_tokens = [];
readdirSync(this.directory).forEach(e => {
if(e == name || e.includes(name)){
matching_tokens.push(this._filename_to_name(e));
}
});
return matching_tokens;
}
/**
* @param {string} name
* @return {string}
* @private
*/
_name_to_filename(name){
return `${join(this.directory, name)}.jwt`;
}
/**
* @param {string} filename
* @return {string}
* @private
*/
_filename_to_name(filename){
return join(filename).split('.')[0];
}
};
module.exports = {
TokenCache,
TokenCacheFilesystem
};