========================================
2022-03-09: better example from https://www.freecodecamp.org/news/javascript-debounce-example/
=================
Debounce?!
A recent increasing popular technique in web applications is an interestng one. It is so called “debounce”. Debouncing in JavaScript is used to limit the rate at which a function can fire. It works by delaying the execution of a function until a certain amount of time has passed without it being called. This can be useful in cases where a function is called multiple times in a short period of time, such as when a user is typing into an input field, and you only want to perform an action after they have finished typing.
A real world analogy
A real world analogy of debounce is to a physical button, once is pressed, it remains in the pressed state (.e.g stays in the housing socket) for a number period of time, during which cannot be pressed again since it is already in pressed down position, before it “bounces” back that can be pressed again.
Javascript implementation
To implement debouncing in JavaScript, you can use a function that sets a timer whenever it is called. If the function is called again before the timer has expired, the timer is cleared and reset, delaying the execution of the function until the timer has expired.
Here is an example of a debounced function in JavaScript:
function debounce(fn, delay) {
let timer;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
In this example, we have a debounce function that takes two arguments: a callback function fn and a delay time delay. It returns a new function that sets a timer whenever it is called, and calls the callback function only if the timer has expired and no further calls have been made to the returned function.
You can use this debounced function as a wrapper for any function that you want to limit the rate of execution.
const debouncedFunction = debounce(myFunction, 1000);
This code creates a new debounced function that calls the myFunction once per second at most.
## Easy Port Testing
## ON SERVER:
dnf --assumeyes install nmap net-tools ; ## in case this has not yet been installed!
systemctl stop firewalld.service ; ## or make the particular port available!
ls -l | /usr/bin/ncat --listen 10000 ;
date | /usr/bin/ncat --listen 3030 ;
<
>
- https://www.cyberciti.biz/faq/unix-linux-check-if-port-is-in-use-command/
lsof -i -P -n | grep LISTEN ; ## make sure ports are listening
netstat -tulpn | grep LISTEN ; ## another way to make sure ports are listening
## ON CLIENT:
just type cmd, and enter ‘telnet’ – this may be necessary: Enable telnet on windows-11 and REBOOT — 2024-10-01
telnet 123.123.123.123 10000 ; ## should give us the directory (or date) and then the ncat command on the server STOPS
- NOTICE ONLY ONE SPACE BETWEEN IP ## and PORT ### !! (this might just be a m$ thing)
## working fake webserver test:
https://jameshfisher.com/2018/12/31/how-to-make-a-webserver-with-netcat-nc/
these did NOT work for me:
(this one requires ‘screen’ whatever that is)
https://support.cpanel.net/hc/en-us/articles/4403282341143-How-to-use-ncat-netcat-as-a-mini-webserver-to-diagnose-network-connectivity-related-issues
https://stackoverflow.com/questions/16640054/minimal-web-server-using-netcat
settings (but not working quite yet)
185.242.5.91 (185.242.5.90 / Protocol: TCP / OpenVpn Port: 443 / OpenConnect Port: 22)
Mark (185.242.5.91 / Protocol: UDP / OpenVpn Port: 443 / OpenConnect Port: 22)
Instructions to run feathers completely from console, no coding!
IMPORTANT: open up a new tab “about:blank”
Click to open a new
about:blank page.
Load the libraries
/* load in libraries */
[ 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js'
, 'https://unpkg.com/@feathersjs/client@^3.0.0/dist/feathers.js'
].forEach( (src) => {
let script = document.createElement('script');
// script.setAttribute('crossOrigin', 'anonymous' ); /* optional */
script.src = src;
// script.type = 'javascript'; /*optional */
script.src = src;
script.async = false;
document.head.appendChild(script); // or document.getElementsByTagName('script')[0];
});
alternative method:
/usr/bin/cat <<END | ncat --listen 80 ;
<!DOCTYPE html>
<html lang="en-us">
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js'></script>
<script src='https://unpkg.com/@feathersjs/client@^3.0.0/dist/feathers.js'></script>
</head>
<body>
fake webserver page. include extra jScript code in the console.
</body>
</html>
END
initialize constants
/* initialize constants */
const app = feathers();
const socket = io(); /* defaults to: const socket = io('ws://localhost:3030'); */
const socket = io('ws://###.###.###.###:3030'); /*if we need to connect to different host */
app.configure(feathers.socketio(socket)); /* PLEASE dont skip this one!! */
/* -- or -- */
app.configure(feathers.socketio(io('ws://###.###.###.###:3030'))); /* just combine the last two lines */
create(POST)
/* create(
POST) */
async function createTiny(name, address) {
let createResult
= await app.service(‘tiny-connect’).create({ name: name, address: address });
console.log(‘createTiny function result: ‘ + JSON.stringify(createResult) );
return createResult;
};
createTiny(‘Mark Edwards’, ’123 Swallow Lane’).then((value) => { /* create(POST) */
console.log(‘createTiny: ‘ + JSON.stringify(value));
})
get(GET) just one using the primary index
/* get(GET) (just one by index) */
async function getTiny(id) { return await app.service(‘tiny-connect’).get(id); };
—- or —-
async function getTiny(id) {
let getResult = await app.service(‘tiny-connect’).get(id);
console.log(‘getTiny function: ‘ + JSON.stringify(getResult) );
return getResult;
};
let getTinyResult = null; let key = 1; // assuming your row ID is one
getTiny(key).then( value => { /* get(GET) */
getTinyResult = value;
console.log(‘getTiny(’ + key + ‘) : ‘ + JSON.stringify(getTinyResult) );
});
/* one line test: */
(async () => { try { let result = await app.service(‘stimword’).get(1) ; console.log(‘the result is: ‘ + JSON.stringify(result)) } catch (e) { console.log(e); } } )() ;
/* or: */
(async () => { let logoutResult = await app.service(‘stimword’).get(2); console.log(logoutResult); })().catch(e => { console.log(e) });
find(GET) all or by query
/* find(GET) (get all or by query) */
async function findTiny(query) {
return await app.service(‘tiny-connect’).find(query);
};
async function findTiny(query) {
console.log(‘Query function using: ‘ + JSON.stringify(query) );
let findResult = await app.service(‘tiny-connect’).find(query);
console.log(‘findTiny function: ‘ + JSON.stringify(findResult) );
return findResult;
};
let findTinyResultAll;
findTiny(null).then((value) => { /* find(GET) (find all) */
findTinyResultAll = value;
console.log(‘findTiny without query: ‘ + JSON.stringify(findTinyResultAll) );
});
let findTinyResultOne; let findDataObject = { ‘query’ :{ id: 0}}; // again, assuming your id is zero!
findTiny(findDataObject).then((value) => { /* find(GET) (with data ) */
findTinyResultOne = value;
console.log(‘findTiny with query: ‘ + JSON.stringify(findTinyResultOne) );
});
/* shortcut examples: */
(async () => { try { let result = await app.service(‘stimword’).find() ; console.log(‘the result is: ‘ + JSON.stringify(result)) } catch (e) { console.log(e); } } ) () ;
(async () => { try { let result = await app.service(‘stimword’).find({‘query’:{‘stimwordWord’:‘horse’}}) ; console.log(‘the result is: ‘ + JSON.stringify(result)) } catch (e) { console.log(e); } } ) () ;
Accessing a KNEX RAW selecxt
SELECT JSON_ARRAYAGG(
JSON_OBJECT(
..........
)
) 'JSON_ARRAYAGG'
.....................................
const result = await knex.raw( sqlStatement, sqlQuery);
return result[0][0].JSON_ARRAYAGG ;
...................................
var newResult;
(async () => { try { let result = await app.service('raw-service').find({query: sqlQuery}) ; newResult = result; } catch (e) { console.log(e); } } )() ;
console.log(JSON.parse(newResult));
patch(PATCH) to update row(s)
/* patch(PATCH) */
async function patchTiny(id, data, params) { /* patch(PATCH) */
let patchResult = await app.service(‘tiny-connect’).patch(id, data, params);
console.log(‘patchTiny function: ‘ + JSON.stringify(patchResult) );
return patchResult;
};
let patchResult = null; let patchKey = 0; let patchData = {address: ’5678 There Street!’ };
patchTiny(patchKey, patchData ).then( value => { /* patch(PATCH) */
patchResult = value;
console.log(‘patchResult: ‘ + JSON.stringify(patchResult));
});
combine create and insert into one promise
async function clientMasterCreate(query) {
return await app.service(‘client-master’).create(query);
};
async function clientMasterFind(query) {
return await app.service(‘client-master’).find( { ‘query’ : query } );
};
clientMasterObj = { ‘layoutName’ : ‘PESL’
, ‘teacherEmail’ : ‘info@englishwithoutaccent.com’
, ‘teacherAutoIncr’ : 385
, ‘clientMasterEmail’ : ‘12yukos@gmail.comX’
};
clientMasterFind(clientMasterObj)
.then( (clientMasterRow) => {
return clientMasterRow
})
.then( (clientMasterRow) => {
if ( clientMasterRow.data.length == 0 ) {
return clientMasterCreate(clientMasterObj)
.then( (clientMasterRow) => {
console.log(‘inserting: ‘ + clientMasterRow.clientMasterAutoIncr );
return clientMasterRow.clientMasterAutoIncr;
})
} else {
return clientMasterRow.data[clientMasterRow.data.length-1].clientMasterAutoIncr;
}
})
.then ((result) => {
console.log(‘clientMasterAutoIncr: ‘ + result)
});
1 – Initialization
latest RH node instructions
older instructions
Install Node version 16 or greater:
dnf module enable nodejs:20 ; ## installing version 20 in this example.
dnf module list nodejs ; ## optional step!
dnf install nodejs ;
node --version ;
npm --version ;
install the feathers command-line interpreter (CLI):
npm install -g feathersjs/cli ; ### or --global
npm install
feathersjs/cli -g ; ### for generating apps (notice g trails…. i think only this one actually works!
npm install feathersjs/cli
4.8.0 —global ; ## 2023-04-19 install a SPECIFIC VERSION!
npm install @feathersjs/feathers —save ; ## save is no longer needed
##https://stackoverflow.com/questions/19578796/what-is-the-save-option-for-npm-install
export PATH=$PATH:/usr/local/bin/ ; ## 2024-09-24 — this was required!
which feathers ; ##
feathersjs —version ; ##
Optionally disable or modify the firewall
systemctl stop firewalld; ## or just authorize port 3030
## --or---
firewall-cmd --add-port=3030/tcp ;.
## --- or----
firewall-cmd --add-port=3030/tcp --permanent ;
Generate an app using the CLI
feathers generate app ; ## notice that NO npm install's are used
npx feathers generate ; ## apparently another method (2024-09-24
feathers generate service ;
## select 'knex' or 'objection' (see below)
### connection string should look like this: "mysql://knexUser:knexPassword@localhost:3306/comptonTransAnlys"
DEBUG=* npm start ; ## test that app starts successfully, optional DEBUG
DEBUG=knex:query npm start ;
2 – modify this files
./config/default.js
"paginate": {
"default": 0,
"max": 0
3a – modify these files for KNEX
./src/services/tester/tester.class.js – KNEX
super({
...options,
name: 'tableName' /* changed this name to reflect the ACTUAL table name */
, id: 'tableNameAutoIncr' /* added this line to reflect the primary key */
});
./src/models/tester.model.js – KNEX
module.exports = function (app) {
return app.get('knexClient'); /* this is MUCH shorter than original! */
};
3b – modify these files for OBJECTION
./src/services/tester/tester.class.js – OBJECTION
add actual table name and unique id column name
model: Model
, 'name' : 'marksTestTable' , 'id' : 'marksTestTableAutoIncr' // line added by mark!!
./src/models/tester.model.js – OBJECTION
line 8: [specify
REAL table name]
static get tableName() {
return 'marksTestTable'; // make SURE table name is correct !! (had to remove underscores)
}
line 14 old: [specify column names in array]
required: ['text'],
line 17 new:
required: ['columnOne', 'columnTwo'], /* original: required: ['text'], */
line 17 old: [specify column types {type: ‘string’ } { type: ‘number’ } { type: ‘timestamp’} ]
text: { type: 'string' }
line 17 new:
'columnOne': { type: 'string' } , 'columnTwo': { type: 'number' } /* original: text: { type: 'string' } */
see: https://knexjs.org/guide/schema-builder.html#date
line 22: comment out lines: ( or add
.slice(0, 19).replace(‘T’, ‘ ‘); to dates.)
$beforeInsert() {
/* original: this.createdAt = this.updatedAt = new Date().toISOString(); */
// or possibly add this:
this.createdAt = this.updatedAt = new Date().toISOString().slice(0, 19).replace(‘T’, ‘ ‘);
$beforeUpdate() {
/* original: this.updatedAt = new Date().toISOString(); */
// or possibly add this:
this.updatedAt = new Date().toISOString().slice(0, 19).replace(‘T’, ‘ ‘);
per https://stackoverflow.com/questions/5129624/convert-js-date-time-to-mysql-datetime
line 35: [ eliminate the ‘create table’ stuff ]
/* most likely db.schema.createTable can be SKIPPED unless a table actually needs to be created this way! */
const db = app.get('knex');
db.schema.hasTable('marksTestTable').then(exists => {
if (!exists) {
console.error('table marksTestTable missing!');
/*
db.schema.createTable('shit', table => {
table.increments('id');
table.string('text');
table.timestamp('createdAt');
table.timestamp('updatedAt');
})
*/
.then(() => console.log('Created shit table')) // eslint-disable-line no-console
.catch(e => console.error('Error creating shit table', e)); // eslint-disable-line no-console
}
})
.catch(e => console.error('Error creating shit table', e)); // eslint-disable-line no-console
it should
ONLY contain:
module.exports = function (app) {
return [class name];
}
4 – Start the app
DEBUG=knex:query npm start ; ## run the app, optional DEBUG
DEBUG=* npm start ; ## run the app, optional DEBUG
5 -Test the app
DONT USE “PUT”, USE “PATCH” INSTEAD!
Postman: Body—->x-www-form-url-urlencoded to POST (create)
POST: http://192.168.123.220:3030/teachers?lastName=Edwards&firstName=Lori (create)
PATCH: http://192.168.123.220:3030/teachers/2/?firstName=Marky (patch)
POST – http://192.168.123.220:3030/tester?columnOne=Column One Value&columnTwo=Column Two Value (add new value)
GET – http://192.168.123.220:3030/tester/1/ ( get by primary id)
GET – http://192.168.123.220:3030/tester/?columnOne=column One Value (find by column value)
GET (find) – be sure “Body—>xx-www-form-urlencode” is blank
GET (get) – be sure “Params” is blank
PATCH – be SURE “Params” is blank, only use “Body—>xx-www-form-urlencode”
## a complicated one!
http://192.168.123.220:3030/tester/
? testerAutoIncr=1
& $select[]=columnTwo
& $select[]=columnOne
& $sort[columnOne]=-1
## will produce:
## select `columnTwo`, `columnOne`, `tester`.`testerAutoIncr` from `tester` where `testerAutoIncr` = ? order by `columnOne`
## https://stackoverflow.com/questions/11889997/how-to-send-an-array-in-url-request/11890080#11890080
## ? col = val1 & col = val2 & col = val3
## will produce:
## WHERE col= ‘val1’ OR col=‘val2’ OR col=‘val3’
### reference: https://dove.feathersjs.com/api/databases/querying.html#select
6 – Create a “before” hook to manipulate the SELECT statement.
feathers “before” hook: [i think that is required by Objection]
./src/hooks/tester.js
// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html
// eslint-disable-next-line no-unused-vars
module.exports = (options = {}) => {
return async context => {
context.params.query = { // added by mark
…context.params.query, // added by mark
$select: [‘columnTwo’,‘columnOne’], // added by mark
$sort: { ‘columnOne’ : 1 } // added by mark
} // added by mark
return context;
};
};
or: (much better:) https://knexjs.org/guide/query-builder.html
module.exports = (options = {}) => {
return async context => {
const query = context.service.createQuery(context.params) ;
// https://knexjs.org/guide/query-builder.html#knex
query .clear(‘select’) // remove ALL existing columns from queryBuilder
.select({ ‘languageNormsName’ : ‘languageNormsName’ } )
// or just .select(‘languageNormsName’)
.distinct()
.orderBy(‘languageNormsName’)
;
context.params.knex = query ;
return context;
};
};
[i think this is required for knex (not objection]
// https://feathersjs.com/api/databases/knex.html#createquery-params
// https://knexjs.org/guide/query-builder.html#clear
// https://stackoverflow.com/questions/47263583/how-can-i-retrieve-unique-values-from-a-column-with-knex-js
/* old fashioned way to clear select statements, use ` .clear('select') ` instead!
for ( index in query._statements.filter ( val => val['grouping'] === 'columns' ) )
{ query._statements.splice(index,1) // remove the "default" column selection
} ;
*/
==========================================================
#####################npm init —yes ;
################npm install @feathersjs/feathers —save ;
######### ???????? npm install knex —save ;
############npm install feathers-knex —save ;