Giter Club home page Giter Club logo

qewd's Introduction

qewd: Quick and Easy Web Developer

Rob Tweed [email protected]
19 May 2023, MGateway Ltd http://www.mgateway.com

Twitter: @rtweed

Google Group for discussions, support, advice etc: http://groups.google.co.uk/group/enterprise-web-developer-community

Thanks to Ward De Backer for debugging assistance and functionality suggestions

What is QEWD?

In summary: QEWD is a Node.js-based platform for developing and running both interactive WebSocket-based applications and REST APIs. QEWD can run as either a monolithic back-end or as a set of MicroServices.

QEWD uses a unique architecture that prevents CPU-intensive or long-running APIs bringing a Node.js system to its knees, and includes QEWD-JSdb, a powerful high-performance, tightly-integrated multi-model database which, uniquely, presents your data as persistent JavaScript Objects.

Try it out

The quickest way to try out QEWD is using the pre-built Docker version.

docker pull rtweed/qewd-server

There's also a Raspberry Pi version

docker pull rtweed/qewd-server-rpi

Create three files within a folder of your choice (eg ~/myQEWDApp), using the sub-folder structure shown below:

    ~/myQEWDApp
        |
        |_ configuration
        |            |
        |            |_ config.json
        |            |
        |            |_ routes.json
        |
        |_ apis
        |    |
        |    |_ helloworld
        |            |
        |            |_ index.js

config.json

  {
    "qewd_up": true
  }

routes.json

  [
    {
      "uri": "/api/helloworld",
      "method": "GET",
      "handler": "helloworld"
    }
  ]

index.js

  module.exports = function(args, finished) {
    finished({
      hello: 'world'
    });
  };

Fire up the QEWD Docker instance:

docker run -it --name qewdup --rm -p 3000:8080 -v ~/myQEWDApp:/opt/qewd/mapped rtweed/qewd-server

or on a Raspberry Pi:

docker run -it --name qewdup --rm -p 3000:8080 -v ~/myQEWDApp:/opt/qewd/mapped rtweed/qewd-server-rpi

Try out your REST API:

http://{{host-ip-address}}:3000/api/helloworld

eg:

http://192.168.1.100:3000/api/helloworld

Further Reading

Baseline QEWD Environment, with Tutorials on devloping REST APIs and Interactive WebSocket-based Applications.

Installing and using QEWD with a native installation of YottaDB

Installing and using QEWD with a networked connection to IRIS

Getting Started with QEWD using QEWD-Up

Information on QEWD-JSdb, the multi-model database that is integrated with QEWD. This is a unique, ground-breaking database abstraction which provides you with on-disk, persistent JavaScript Objects with which you directly interact via JavaScript.

License

Copyright (c) 2023 MGateway Ltd,
Banstead, Surrey UK.
All rights reserved.

http://www.mgateway.com
Email: [email protected]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0                           

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and
limitations under the License.

qewd's People

Contributors

robtweed avatar shabiel avatar wdbacker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qewd's Issues

Unexpected token error in qewd start

Hi, I am getting the following error while starting QEWD sample application can you please help?

Node version v4.2.6

/home/aby/qewd/node_modules/qewd/lib/worker.js:54
this.db.use = function(documentName, ...subscripts) {
^^^
SyntaxError: Unexpected token ...
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:374:25)
at Object.Module._extensions..js (module.js:417:10)
at Module.load (module.js:344:32)
at Function.Module._load (module.js:301:12)
at Module.require (module.js:354:17)
at require (internal/module.js:12:17)
at Object. (/home/aby/qewd/node_modules/qewd/lib/qewd.js:31:14)
at Module._compile (module.js:410:26)
at Object.Module._extensions..js (module.js:417:10)

Unable to run docker image

HI, I have followed the instructions in the Readme to deploy the docker image, but when it starts the following errors occur.

Please advise.

:52
2023-05-27 14:39:52 > [email protected] start /opt/qewd
2023-05-27 14:39:52 > NODE_PATH=/opt/qewd/mapped/modules node qewd.js
2023-05-27 14:39:52
2023-05-27 14:39:52 internal/modules/cjs/loader.js:883
2023-05-27 14:39:52 throw err;
2023-05-27 14:39:52 ^
2023-05-27 14:39:52
2023-05-27 14:39:52 Error: Cannot find module '/opt/qewd/mapped/startup'
2023-05-27 14:39:52 Require stack:
2023-05-27 14:39:52 - /opt/qewd/qewd.js
2023-05-27 14:39:52 at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)
2023-05-27 14:39:52 at Function.Module._load (internal/modules/cjs/loader.js:725:27)
2023-05-27 14:39:52 at Module.require (internal/modules/cjs/loader.js:952:19)
2023-05-27 14:39:52 at require (internal/modules/cjs/helpers.js:88:18)
2023-05-27 14:39:52 at Object. (/opt/qewd/qewd.js:103:13)
2023-05-27 14:39:52 at Module._compile (internal/modules/cjs/loader.js:1063:30)
2023-05-27 14:39:52 at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
2023-05-27 14:39:52 at Module.load (internal/modules/cjs/loader.js:928:32)
2023-05-27 14:39:52 at Function.Module._load (internal/modules/cjs/loader.js:769:14)
2023-05-27 14:39:52 at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12) {
2023-05-27 14:39:52 code: 'MODULE_NOT_FOUND',
2023-05-27 14:39:52 requireStack: [ '/opt/qewd/qewd.js' ]
2023-05-27 14:39:52 }
2023-05-27 14:39:52 npm ERR! code ELIFECYCLE
2023-05-27 14:39:52 npm ERR! errno 1
2023-05-27 14:39:52 npm ERR! [email protected] start: NODE_PATH=/opt/qewd/mapped/modules node qewd.js
2023-05-27 14:39:52 npm ERR! Exit status 1
2023-05-27 14:39:52 npm ERR!
2023-05-27 14:39:52 npm ERR! Failed at the [email protected] start script.
2023-05-27 14:39:52 npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
2023-05-27 14:39:52
2023-05-27 14:39:52 npm ERR! A complete log of this run can be found in:
2023-05-27 14:39:52 npm ERR! /root/.npm/_logs/2023-05-27T13_39_52_916Z-debug.log

loadModule can be simplied

qewd/lib/appHandler.js

Lines 43 to 45 in 6b9b898

if (!appModule.handlers) appModule.handlers = {};
if (appModule.handlers) this.handlers[application] = appModule.handlers;

to

if (!appModule.handlers) appModule.handlers = {}; 
  
this.handlers[application] = appModule.handlers; 

because if (appModule.handlers) on line 45 is always true

data is null

Hi @robtweed

Please take a look

qewd/lib/sockets.js

Lines 72 to 77 in cbf87ac

if (typeof data !== 'object') {
console.log('Received message is not an object: ' + data);
return;
}
var type = data.type;
if (!type) {

if data is null, the code will fail on line 76

Question about config/params

HI @robtweed

qewd/lib/master.js

Lines 119 to 124 in b74a7c5

if (params.resilientMode) {
config.resilientMode = {
documentName: params.resilientMode.queueDocumentName || 'ewdQueue',
keepPeriod: params.resilientMode.keepPeriod || 3600
}
}

Should it be if (params.resilientMode) { or if (config.resilientMode) {?

`beforeRouter`/`afterRouter` not needed to be validated for isArray

Hi @robtweed

Please consider the following lines:

qewd/lib/master-express.js

Lines 95 to 102 in cbf87ac

var options = {
nextCallback: route.afterRouter ? true : false
};
args = args.concat([qx.router(options)]);
// add array with custom middleware to add after qx.router is called (if present)
if (route.afterRouter && Array.isArray(route.afterRouter)) args = args.concat(route.afterRouter);

Some notes:

  1. I'm not sure that beforeRouter/afterRouter must be validated to be an array because concat works fine if passed argument can be added to existing array -> [1, 2].concat(3) => [1, 2, 3]
  2. Another note is that, if we keep the current structure and declare beforeRouter/afterRouter as NOT an array, nextCallback can be set incorrectly (it will be true) but afterRouter won't be added

My proposal here is to allow developers to pass afterRouter as function of array of functions. In this case we do not need to validate beforeRouter/afterRouter for Array.isArray

serviceModuleLoad error handling never called

Hi @robtweed

The catch block in the following example is never called because loadModule function catch issue inside and returns boolean depends on the results.

qewd/lib/appHandler.js

Lines 356 to 371 in 71a0457

try {
//this.handlers[service] = require(service).handlers;
var ok = loadModule.call(this, service, finished);
if (!ok) return;
//console.log('service module loaded for ' + service);
}
catch(err) {
error = 'Unable to load handler module: ' + service;
if (this.errorMessages && this.errorMessages[application] && this.errorMessages[application]['serviceModuleLoad']) error = this.errorMessages[application]['serviceModuleLoad'];
console.log(error + ': ' + err);
finished({
error: error,
reason: err,
service: service
});
return;

I think the correct one here is:

  1. pass information about service load to loadModule
  2. move serviceModuleLoad error handling inside loadModule function
ok = loadModule.call(this, service, finished);
if (!ok) return;

NOTE:
The code above doesn't pass information about service or application to loadModule

Please provide your thoughts...

File upload in not working in qewd

Hi,
I want to create a new API for file upload in QEWD Application. I have defined the API in routes and called the API from postman but the request does not have the form-data/multipart file.
Thanks.

unused function

qewd/lib/jwtHandler.js

Lines 433 to 440 in 736580d

function isTokenAPossibleJWT(token) {
var pieces = token.split('.');
if (pieces.length !== 3) return false;
if (pieces[0].length < 2) return false;
if (pieces[1].length < 2) return false;
if (pieces[2].length < 2) return false;
return true;
}

Question about DELETE route for master-koa

Hi @robtweed

Please take a look:

qewd/lib/master-koa.js

Lines 151 to 164 in b74a7c5

match = 'POST ' + path + '/:type/*';
koaRouter.addRoute(match, args);
match = 'POST ' + path + '/:type';
koaRouter.addRoute(match, args);
match = 'DELETE ' + path + '/:type/*';
koaRouter.addRoute(match, args);
match = 'PUT ' + path + '/:type';
koaRouter.addRoute(match, args);
match = 'PUT ' + path + '/:type/*';
koaRouter.addRoute(match, args);

All verbs are used two times for '/:type and '/:type/*' but DELETE is used only once (for '/:type/*'). Is it correct?

Qewd-up without websockets?

Is it possible to configure the Orchestrator to forward incoming REST requests to a microservice using REST calls rather than websocket messages?

We are trying to integrate a non-Qewd service into an existing Qewd-up deployment.

Many thanks.
G

return true if success

Hi @robtweed

qewd/lib/worker.js

Lines 78 to 91 in b74a7c5

this.setCustomErrorResponse = function(params) {
var application = params.application;
if (!application || application === '') return false;
var type = params.errorType;
if (!type || type === '') return false;
var text = params.text || 'Unspecified Error';
var statusCode = params.statusCode || '400';
if (!this.errorMessages) this.errorMessages = {};
if (!this.errorMessages[application]) this.errorMessages[application] = {};
this.errorMessages[application][type] = {
text: text,
statusCode: statusCode
};
};

I think it would be good if setCustomErrorResponse returns true on success

Passing this to beforeMicroServiceHandler

Hi @robtweed

qewd/lib/appHandler.js

Lines 49 to 52 in b74a7c5

if (appModule.beforeMicroServiceHandler) {
this.beforeHandlers[application] = function(messageObj, session, send, finished) {
return appModule.beforeMicroServiceHandler.call(this, messageObj, finished);
};

Looks like this passes to beforeMicroServiceHandler is not the same this that passed to appModule.init for example. Could you please take a look?

Handle boolean property correctly

Hey @robtweed

qewd/lib/jwtHandler.js

Lines 330 to 340 in 71a0457

function getProperty(propertyName, token) {
var payload;
try {
payload = jwt.decode(token, null, true);
}
catch(err) {
return false;
}
if (!payload[propertyName]) return false;
return payload[propertyName];
}

What if payload contains false, '' or null? In this case false be returned by getProperty. Maybe it's better to have here if (typeof payload[propertyName] === 'undefined'? Ofc if it makes sense here

docker vs. docker-server

Hi Rob,

Could you please explain the different intent for the docker and docker-server folders? (Or perhaps add readme files to those folders.)

A problem with custom routes

Motivation

I'd like to add a simple module that provided with healthcheck functionality (actually, it's part of integration testing for ripple qewd microservices).

The module should look very simple:
qewd.js

'use strict';

const qewd = require('qewd').master;
const path = require('path');

const config = {
  managementPassword: 'keepThisSecret!',
  serverName: 'My QEWD Server',
  port: 8080,
  database: {
    type: 'redis'
  }
};
const routes = [
  {
    path: '/',
    module: path.join(__dirname, 'healthcheck')
  }
];

qewd.start(config, routes);

healthcheck.js

function healthcheck(args, finished) {
  finished({
    ok: true,
    timestamp: Date.now()
  });
}

module.exports = {
  restModule: true,

  init: function (application) {
    const routes = [
      {
        url: '/healthcheck',
        method: 'GET',
        handler: healthcheck
      }
    ];

    router.initialise(routes, module.exports);
    router.setErrorResponse(404, 'Not Found');

    this.setCustomErrorResponse.call(this, {
      application: application,
      errorType: 'noTypeHandler',
      text: 'Resource Not Found',
      statusCode: '404'
    });
  }
};

Problem

It returns 404

Investigation

I started to look deeper at the problem to see why such a simple case doesn't work. After research master-express, qewd-router and qx.router logic I found the following:
routes.path is used as first parameter to pass in express middleware app.use the following way

app.use(routes.path, beforeRouter, qx.router(options), afterRouter)

That means routes.path should be / to work correctly (see example with 2 routers on root path). However, this leads to another problem. GET /healthcheck returns {"error":"Missing authorization header"}

Looking at express params I found the following:

{
  "type": "ewd-qoper8-express",
  "path": "/healthcheck",
  "method": "GET",
  "headers": {
    "host": "127.0.0.1:8085",
    "connection": "keep-alive",
    "cache-control": "max-age=0",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
    "dnt": "1",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-US,en;q=0.9,ru;q=0.8,de;q=0.7",
    "cookie": "io=gT4u07XmDNfb7crjAAAF"
  },
  "params": {
    "type": "healthcheck"
  },
  "query": {},
  "body": {},
  "ip": "::ffff:172.17.0.1",
  "ips": []
}

And again there are 2 problems here now

  1. module map looks like {"":"/opt/qewd/healthcheck"} that it's bad
  2. application prop is missed for messageObj and corresponding app handler logic is not called

Solution

  1. Update qewd master module map initialization to use application parameter when path is /
  2. I found that application and expressType parameters are not passed to router here. If we update corresponding master-express and master-koa logic, this problem will be gone.

Now messageObj looks correctly

  1. module map is initialized correctly -> moduleMap":{"healthcheck":"/opt/qewd/healthcheck"}
  2. messageObj looks correctly to be loaded by restModules
{
  "type": "ewd-qoper8-express",
  "path": "/healthcheck",
  "method": "GET",
  "headers": {
    "host": "127.0.0.1:8085",
    "connection": "keep-alive",
    "cache-control": "max-age=0",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
    "dnt": "1",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate, br",
    "accept-language": "en-US,en;q=0.9,ru;q=0.8,de;q=0.7",
    "cookie": "io=gT4u07XmDNfb7crjAAAF"
  },
  "params": {
    "type": "healthcheck"
  },
  "query": {},
  "body": {},
  "ip": "::ffff:172.17.0.1",
  "ips": [],
  "application": "healthcheck",
  "expressType": "healthcheck"
}

However, the module could not be loaded due to the problem with processing name for handler in qewd-router. This should be patched also. If it be patched for this particular case, all will work. The final healthcheck module code is the following: https://gist.github.com/killmenot/44f03452b04066f97baa28ad91f6e279

Docker Hub name is wrong in README?

Hi Rob,

I'm trying to have a go with QEWD-UP. The read me shows as attached:
Screenshot 2021-04-02 at 14 45 01

  1. I searched docker hub but couldn't find anything with that name?
  2. Also you have a double parenthesis in that bit of README that could probably use a tidy-up :)

bug with resilient mode and gtm

Hey @robtweed

Simple test should be able to send message using websockets:

describe('custom message', () => {
    let data;

    beforeEach((done) => {
      request.
        post('/ajax').
        send({
          type: 'ewd-register',
          application: 'test-app'
        }).
        end((err, res) => {
          if (err) return done.fail(err);

          data = {
            type: 'test',
            token: res.body.token,
            params: {
              text: 'Hello world'
            }
          };

          done();
        });
    });

    it('should be able to send message using websockets', (done) => {
      const socket = io.connect('ws://localhost:8080');

      socket.on('connect', () => socket.emit('ewdjs', data));
      socket.on('ewdjs', (responseObj) => {
        socket.disconnect();

        expect(responseObj).toEqual({
          type: 'test',
          finished: true,
          message: {
            text: 'You sent: Hello world via express'
          },
          responseTime: jasmine.stringMatching(/^\d*ms$/)
        });

        done();
      });
    });
  });

QEWD configuration:

  • express
  • gtm
  • resilientMode

Output:
screenshot 2017-11-28 02 44 44

QEWD configuration:

  • express
  • redis
  • resilientMode

screenshot 2017-11-28 02 45 14

Question

Hey @robtweed

Could you please explain me the logic of the following lines?

qewd/lib/appHandler.js

Lines 303 to 305 in 71a0457

if (messageObj.service && !this.servicesAllowed[application]) {
var ok = loadModule.call(this, application, finished);
if (!ok) return;

is it correct to pass application to loadModule? I have a feeling that messageObj.service should be passed there

A problem with processing array responses with workerResponseHandlers

Hi @robtweed

Just a note for you. I believe you may know about this problem and know what to do (if it's needed ofc)

We just have found a "problem" with processing array responses with microservices logic that related with

  • qewd
  • qewd-router

Related snippets of code
(1): https://github.com/robtweed/qewd/blob/master/lib/appHandler.js#L464-L478
(2): https://github.com/robtweed/qewd-router/blob/master/lib/router.js#L223-L226

Scenario 1 - object response: (problem is not reproduced)

When object response returned it goes to (2) and path is set correctly
Example:

{
  foo: 'bar'
  path: '/api/test'
}

Scenario 2 - array response:

When array response returned it goes to (2) and path is set the following way
Example:

[
 {
   foo: 'bar1'
 },
 {
   foo: 'bar2'
 },
 path: '/api/test'
]

Only after that, the logic goes to (1) to line 469
if (messageObj.ms_requestId && Array.isArray(results)) results = {results: results};

This problem appears with worker response logic because there is no path in messageObj:

if (workerResponseHandler[message.path]) {

HOW TO FIX IT

The quick solution is to use your pattern when you return array response the following way:

{
api: 'getRespectFormVersions',
use: 'results',
results: [
{
foo: 'bar1'
},
{
foo: 'bar2'
},
]
}

However, I believe that it's better to patch qewd-router (2) to process array responses correctly there

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.