API client in Javascript - javascript

I need a little help to solve a problem in my project.
Scenario:
First: I have a SPA web site that is being developed in Vue.js.
Second: I also have a Web API spec in Swagger that I want to use to generate my client code in Javascript.
Lastly: I'm using swagger-codegen-cli.jar for that.
What I've done until now
1 - Download the last swagger-codegen-cli.jar stable version with javascript support:
curl http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/2.4.7/swagger-codegen-cli-2.4.7.jar -o swagger-codegen-cli.jar
2 - Generate the client code using:
java -jar swagger-codegen-cli.jar generate -i http://192.168.0.85:32839/api/swagger/v1/swagger.json -l javascript -o ./web_api_client/
3 - Add the generated module to my project:
"dependencies": {
// ...
"vue": "^2.6.10",
"vue-router": "^3.0.3",
"web_api_client": "file:./web_api_client"
},
4 - Execute npm install. Apparently, it's working fine.
5 - At this moment I faced the problem. For some reason, the module generated isn't loaded completely.
export default {
name: 'home',
components: {
HelloWorld
},
mounted() {
var WebApiClient = require("web_api_client");
var defaultClient = WebApiClient.ApiClient.instance;
var oauth2 = defaultClient.authentications["oauth2"];
oauth2.accessToken = "YOUR ACCESS TOKEN";
var apiInstance = new WebApiClient.VersaoApi();
var callback = function(error, data, response) {
if (error) {
console.error(error);
} else {
console.log('API called successfully. Returned data: ' + data);
}
};
apiInstance.apiVersaoGet(callback);
}
}
6 - The line var WebApiClient = require("web_api_client"); is working without any error, however, not working 100%. The instance of the module has been created but empty. For instance, WebApiClient.ApiClient is always undefined.
7 - I took a look at the generated code and I think the problem is related with the way the module is being loaded.
(function(factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['ApiClient', 'api/VersaoApi'], factory);
} else if (typeof module === 'object' && module.exports) {
// CommonJS-like environments that support module.exports, like Node.
module.exports = factory(require('./ApiClient'), require('./api/VersaoApi'));
}
}(function(ApiClient, VersaoApi) {
'use strict';
// ...
In this code, neither of ifs blocks are executed.
Has someone faced a problem like that?
Some advice?
Many thanks, folks.

Solution
After a while trying to fix the problem with require("web_api_client"); I decided to use ES6 instead ES5.
I found an option in swagger-codegen-cli.jar to generate the client code using ES6 as shown below:
java -jar swagger-codegen-cli.jar generate -i http://192.168.0.85:32839/api/swagger/v1/swagger.json -l javascript --additional-properties useES6=true -o ./web_api_client/
Using ES6 I was able to import the javascript module direct from the generated source as shown in the code below.
import WebApiClient from "./web_api_client/src/index";
let defaultClient = WebApiClient.ApiClient.instance;
defaultClient.basePath = 'http://192.168.0.85:32839';
// Configure OAuth2 access token for authorization: oauth2
let oauth2 = defaultClient.authentications["oauth2"];
oauth2.accessToken = "YOUR ACCESS TOKEN";
let apiInstance = new WebApiClient.VersaoApi();
apiInstance.apiVersaoGet((error, data, response) => {
if (error) {
console.error(error);
} else {
console.log("API called successfully. Returned data: " + data + response);
}
});
When I first ran the code I got an error because the module WebApiClient generated didn't have the keyword default in the export block.
Original generated code
export {
/**
* The ApiClient constructor.
* #property {module:ApiClient}
*/
ApiClient,
// ...
Alter changed
export default {
/**
* The ApiClient constructor.
* #property {module:ApiClient}
*/
ApiClient,
// ...
Now everything is working fine.

Related

'X Is not a function' in CommonJS

I've got the following code I transformed from a Trypescript, ESM-syntax based file to a Javascript, CJS-syntax file.
const apiClientFactory = require("#vue-storefront/core");
function onCreate(settings) {
return {
config: settings,
client: {},
};
}
const getPrice = () => {
console.log("$55,98")
}
const { createApiClient } = apiClientFactory({
onCreate,
api: {
getPrice,
},
});
module.exports = {
createApiClient,
};
I can not seem to find if the error "apiClientFactory is not a function" originates from old ESM-based code. Or that the function isn't called properly. However, apiClientFactory is correctly imported (ESM syntax)
What are you trying to achieve with this?
Because the whole Vue Storefront project uses TypeScript, so I recommend you to use it and follow the procedures and code standards that we are using.
To find a good example on the API please check the code for the integrations of Magento or Vendure.

How to correctly configure unit tests for Probot with Scheduler Extension?

I am using the following minimal probot app and try to write Mocha unit tests for it.
Unfortunately, it results in the error below, which indicates that some of my setup for the private key or security tokens is not picked up.
I assume that the configuration with my .env file is correct since I do not get the same error when I start the probot via probot-run.js.
Are there any extra steps needed to configure probot when used with Mocha?
Any suggestions on why the use of the scheduler extension may result in such issue would be great.
Code and error below:
app.ts
import createScheduler from "probot-scheduler";
import { Application } from "probot";
export = (app: Application) => {
createScheduler(app, {
delay: !!process.env.DISABLE_DELAY, // delay is enabled on first run
interval: 24 * 60 * 60 * 1000 // 1 day
});
app.on("schedule.repository", async function (context) {
app.log.info("schedule.repository");
const result = await context.github.pullRequests.list({owner: "owner", repo: "test"});
app.log.info(result);
});
};
test.ts
import createApp from "../src/app";
import nock from "nock";
import { Probot } from "probot";
nock.disableNetConnect();
describe("my scenario", function() {
let probot: Probot;
beforeEach(function() {
probot = new Probot({});
const app = probot.load(createApp);
});
it("basic feature", async function() {
await probot.receive({name: "schedule.repository", payload: {action: "foo"}});
});
});
This unfortunately results in the following error:
Error: secretOrPrivateKey must have a value
at Object.module.exports [as sign] (node_modules/jsonwebtoken/sign.js:101:20)
at Application.app (node_modules/probot/lib/github-app.js:15:39)
at Application.<anonymous> (node_modules/probot/lib/application.js:260:72)
at step (node_modules/probot/lib/application.js:40:23)
at Object.next (node_modules/probot/lib/application.js:21:53)
Turns out that new Probot({}); as suggested in the documentation initializes the Probot object without any parameters (the given options object {} is empty after all).
To avoid the error, one can provide the information manually:
new Probot({
cert: "...",
secret: "...",
id: 12345
});

How to add authorization header when runtime import webpack chunks of Vue components

The purpose of this task is to make it impossible to download the Vue-component package (*.js file) knowing the address of the component, but not having an access token.
I'm developing an access control system and a user interface in which the set of available components depends on the user's access level.
The system uses the JSON API and JWT authorization. For this, Axios is used on the client side. To build the application, we use Webpack 4, to load the components, we use the vue-loader.
After the user is authorized, the application requests an array of available routes and metadata from the server, then a dynamically constructed menu and routes are added to the VueRouter object.
Below I gave a simplified code.
import axios from 'axios'
import router from 'router'
let API = axios.create({
baseURL: '/api/v1/',
headers: {
Authorization: 'Bearer mySecretToken12345'
}
})
let buildRoutesRecursive = jsonRoutes => {
let routes = []
jsonRoutes.forEach(r => {
let path = r.path.slice(1)
let route = {
path: r.path,
component: () => import(/* webpackChunkName: "restricted/[request]" */ 'views/restricted/' + path)
//example path: 'dashboard/users.vue', 'dashboard/reports.vue', etc...
}
if (r.children)
route.children = buildRoutesRecursive(r.children)
routes.push(route)
})
return routes
}
API.get('user/routes').then(
response => {
/*
response.data =
[{
"path": "/dashboard",
"icon": "fas fa-sliders-h",
"children": [{
"path": "/dashboard/users",
"icon": "fa fa-users",
}, {
"path": "/dashboard/reports",
"icon": "fa fa-indent"
}
]
}
]
*/
let vueRoutes = buildRoutesRecursive(response.data)
router.addRoutes(vueRoutes)
},
error => console.log(error)
)
The problem I'm having is because Webpack loads the components, by adding the 'script' element, and not through the AJAX request. Therefore, I do not know how to add an authorization header to this download. As a result, any user who does not have a token can download the code of the private component by simply inserting his address into the navigation bar of the browser.
Ideally, I would like to know how to import a vue component using Axios.
Or, how to add an authorization header to an HTTP request.
I needed something similar and came up with the following solution. First, we introduce a webpack plugin that gives us access to the script element before it's added to the DOM. Then we can munge the element to use fetch() to get the script source, and you can craft the fetch as needed (e.g. add request headers).
In webpack.config.js:
/*
* This plugin will call dynamicImportScriptHook() just before
* the script element is added to the DOM. The script object is
* passed to dynamicImportScriptHook(), and it should return
* the script object or a replacement.
*/
class DynamicImportScriptHookPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"DynamicImportScriptHookPlugin", (compilation) =>
compilation.mainTemplate.hooks.jsonpScript.tap(
"DynamicImportScriptHookPlugin", (source) => [
source,
"if (typeof dynamicImportScriptHook === 'function') {",
" script = dynamicImportScriptHook(script);",
"}"
].join("\n")
)
);
}
}
/* now add the plugin to the existing config: */
module.exports = {
...
plugins: [
new DynamicImportScriptHookPlugin()
]
}
Now, somewhere convenient in your application js:
/*
* With the above plugin, this function will get called just
* before the script element is added to the DOM. It is passed
* the script element object and should return either the same
* script element object or a replacement (which is what we do
* here).
*/
window.dynamicImportScriptHook = (script) => {
const {onerror, onload} = script;
var emptyScript = document.createElement('script');
/*
* Here is the fetch(). You can control the fetch as needed,
* add request headers, etc. We wrap webpack's original
* onerror and onload handlers so that we can clean up the
* object URL.
*
* Note that you'll probably want to handle errors from fetch()
* in some way (invoke webpack's onerror or some such).
*/
fetch(script.src)
.then(response => response.blob())
.then(blob => {
script.src = URL.createObjectURL(blob);
script.onerror = (event) => {
URL.revokeObjectURL(script.src);
onerror(event);
};
script.onload = (event) => {
URL.revokeObjectURL(script.src);
onload(event);
};
emptyScript.remove();
document.head.appendChild(script);
});
/* Here we return an empty script element back to webpack.
* webpack will add this to document.head immediately. We
* can't let webpack add the real script object because the
* fetch isn't done yet. We add it ourselves above after
* the fetch is done.
*/
return emptyScript;
};
Although sspiff's answer looks quite promising, it did not work directly for me.
After some investigation this was mainly due to me using Vue CLI 3 and thus a newer version of webpack. (which is kinda weird as sspiff mentioned using webpack 4.16.1).
Anyway to solve it I used the following source: medium.com,
Which gave me the knowledge to edit the given code.
This new code is situated in vue.config.js file:
/*
* This plugin will call dynamicImportScriptHook() just before
* the script element is added to the DOM. The script object is
* passed to dynamicImportScriptHook(), and it should return
* the script object or a replacement.
*/
class DynamicImportScriptHookPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"DynamicImportScriptHookPlugin", (compilation) =>
compilation.mainTemplate.hooks.render.tap(
{
name: "DynamicImportScriptHookPlugin",
stage: Infinity
},
rawSource => {
const sourceString = rawSource.source()
if (!sourceString.includes('jsonpScriptSrc')) {
return sourceString;
} else {
const sourceArray = sourceString.split('script.src = jsonpScriptSrc(chunkId);')
const newArray = [
sourceArray[0],
'script.src = jsonpScriptSrc(chunkId);',
"\n\nif (typeof dynamicImportScriptHook === 'function') {\n",
" script = dynamicImportScriptHook(script);\n",
"}\n",
sourceArray[1]
]
return newArray.join("")
}
}
)
);
}
}
module.exports = {
chainWebpack: (config) => {
config.plugins.delete('prefetch')
},
configureWebpack: {
plugins: [
new DynamicImportScriptHookPlugin()
]
}
}
The second piece of code provided by sspiff has stayed the same and can be placed in the App.vue file or the index.html between script tags.
Also to further improve this answer I will now explain how to split the chunks in Vue CLI 3 for this specific purpose.
as you can see I also added the chainWebpack field to the config. This makes sure that webpack does not add prefetch tags in the index.html. (e.g. it will now only load lazy chunks when they are needed)
To further improve your splitting I suggest changing all your imports to something like:
component: () => import(/* webpackChunkName: "public/componentName" */ /* webpackPrefetch: true */'#/components/yourpubliccomponent')
component: () => import(/* webpackChunkName: "private/componentName" */ /* webpackPrefetch: false */'#/components/yourprivatecomponent')
This will make sure that all your private chunks end up in a private folder and that they will not get prefetched.
The public chunks will end up in a public folder and will get prefetched.
For more information use the following source how-to-make-lazy-loading-actually-work-in-vue-cli-3
Hope this helps anyone with this problem!
To perform a simple component download using an access token, you can do the following...
1) Use asynchronous component loading with file extraction. Use webpackChunkName option to separate file or directory/file, like:
components: {
ProtectedComp: () => import(/* webpackChunkName: "someFolder/someName" */ './components/protected/componentA.vue')
}
2) Configure server redirection for protected files or direcory. Apache htaccess config for example:
RewriteRule ^js/protected/(.+)$ /js-provider.php?r=$1 [L]
3) write a server-side script that checks the token in the header or cookies and gives either the contents of .js or 403 error.

Angular 4 - How to Simulate Mock Data for Prototyping and Development

I'm in the process of upgrading an AngularJS v1.5 project to Angular 4.x. During development of the original AngularJS application, we would use the ngMocks package to simulate actual web service API responses, and display the data accordingly on the page. This was incredibly helpful during development as I didn't have to hard-code values for removal later on. Best of all, we configured Webpack to only include the mock data during development, and ignore those mock data files when building our application for production use. The mock data was configured like this:
/* app-login.mock.js */
import angular from 'angular';
import 'angular-mocks';
angular.module('app').run(function ($httpBackend) {
$httpBackend
.whenPOST('./api/auth')
.respond(function(method, url, data) {
var credentials = angular.fromJson(data);
if (credentials.username == 'gooduser') {
return [200, {'token': createToken(credentials.username)}];
} else {
return [401, {'errorMsg': 'Mock login only allows username "gooduser"'}];
}
});
});
function createToken(username) {
// Create a token, which is valid enough for testing purposes.
// This token is based on the actual token generated by the web service.
let currentTime = new Date();
let futureTime = new Date(currentTime.getTime() + ((currentTime.getHours() + 8) * 60 * 60 * 1000));
let header = {
alg: 'HS512'
};
let payload = {
exp: futureTime.getTime() / 1000,
sub: username,
roles: 'SOME_APPLICATION_ROLES',
iat: currentTime.getTime() / 1000
};
return `${btoa(angular.toJson(header))}.${btoa(angular.toJson(payload))}`;
}
Webpack was then configured to include all "mock" files into the built bundle, which could then be displayed as if it were a real HTTP response.
/* webpack.config.js */
const isProd = process.env.NODE_ENV === 'production';
const entry = {
app: (() => {
let app = [
'babel-polyfill',
path.join(PATHS.app, 'pollyfills.ts'),
path.join(PATHS.app, 'main.ts')
];
if (isProd) {
app.push(path.join(PATHS.app, 'app.prod.js'));
} else {
app.push(path.join(PATHS.app, 'app.mock.js'));
}
return app;
})()
};
module.exports = {
entry,
// ...other exports
};
And then the app.mock.js file:
/* app.mock.js */
var mockContext = require.context(".", true, /\.mock$/);
mockContext.keys().forEach(mockContext);
I've scoured the internet looking for a solution that works just as well as our old one, though I haven't come up with any good answers. Best I've found are tutorials on how to set up Unit Tests that return mock data, and while that's useful for testing functionality it doesn't help me test the application during the development process.
I also have seen some documentation on setting up Interceptors using the new HttpClient class found within Angular 4, but I'm not sure how to add it to our Webpack configuration under the condition of only being allowed during development. Does anyone have any advice on what to do?
I use the angular-in-memory-web-api. You can find it here: https://github.com/angular/in-memory-web-api
UPDATE: The repo was moved here, within the angular/angular repo: https://github.com/angular/angular/tree/e0dfa42d6e656124f3c3d78e178b1bf091b38e79/packages/misc/angular-in-memory-web-api
It intercepts all of your http calls and works with sample data you provide.
To change from dev to production, you need to remove the imports. Or you could possibly write two different modules, one with the dev imports and one with the production imports and include one or the other with webpack similar to what you do now. (But I have not tried this.)
You set up your data like this:
import { InMemoryDbService } from 'angular-in-memory-web-api';
import { IProduct } from './product';
export class ProductData implements InMemoryDbService {
createDb() {
let products: IProduct[] = [
{
'id': 1,
'productName': 'Leaf Rake',
'productCode': 'GDN-0011',
'releaseDate': 'March 19, 2016',
'description': 'Leaf rake with 48-inch wooden handle.',
'price': 19.95,
'starRating': 3.2,
'imageUrl': 'http://openclipart.org/image/300px/svg_to_png/26215/Anonymous_Leaf_Rake.png',
'tags': ['rake', 'leaf', 'yard', 'home']
},
// ...
];
return { products };
}
}
And you build your data access service using the normal Http or HttpClient.
I have a full example with all CRUD operations here: https://github.com/DeborahK/Angular2-ReactiveForms

hapi.js best way to handle errors

I'm creating my first node.js REST web service using hapi.js. I'm curious as to the best way to handle errors let's say from my dao layer. Do i throw them in my dao layer and then just try/catch blocks to handle them and send back errors in my controller, or is there a better way that the cool kids are handling this?
routes/task.js
var taskController = require('../controllers/task');
//var taskValidate = require('../validate/task');
module.exports = function() {
return [
{
method: 'POST',
path: '/tasks/{id}',
config : {
handler: taskController.createTask//,
//validate : taskValidate.blah
}
}
]
}();
controllers/task.js
var taskDao = require('../dao/task');
module.exports = function() {
return {
/**
* Creates a task
*
* #param req
* #param reply
*/
createTask: function createTask(req, reply) {
taskDao.createTask(req.payload, function (err, data) {
// TODO: Properly handle errors in hapi
if (err) {
console.log(err);
}
reply(data);
});
}
}();
dao/task.js
module.exports = function() {
return {
createTask: function createTask(payload, callback) {
... Something here which creates the err variable...
if (err) {
console.log(err); // How to properly handle this bad boy
}
}
}();
Generic Solution w/ Fully Customisable Error Template/Messages
We wrote a Hapi Plugin that handles all errors seamlessly: npmjs.com/package/hapi-error
It lets you define your own custom error pages in 3 easy steps.
1. Install the plugin from npm:
npm install hapi-error --save
2. Include the plugin in your Hapi project
Include the plugin when you register your server:
server.register([require('hapi-error'), require('vision')], function (err) {
// your server code here ...
});
See: /example/server_example.js for simple example
3. Ensure that you have a View called error_template
Note: hapi-error plugin expects you are using Vision (the standard view rendering library for Hapi apps)
which allows you to use Handlebars, Jade, React, etc. for your templates.
Your error_template.html (or error_template.ext error_template.jsx) should make use of the 3 variables it will be passed:
errorTitle - the error tile generated by Hapi
statusCode - *HTTP statusCode sent to the client e.g: 404 (not found)
errorMessage - the human-friendly error message
for an example see: /example/error_template.html
That's it! Now your Hapi App handles all types of errors and you can throw your own custom ones too!
Note: hapi-error works for REST/APIs too. if the content type header (headers.acceps) is set to application/json then your app will return a JSON error to the client, otherwise an HTML page will be served.
In doing more research along with Ricardo Barros' comment on using Boom, here's what I ended up with.
controllers/task.js
var taskDao = require('../dao/task');
module.exports = function() {
return {
/**
* Creates a task
*
* #param req
* #param reply
*/
createTask: function createTask(req, reply) {
taskDao.createTask(req.payload, function (err, data) {
if (err) {
return reply(Boom.badImplementation(err));
}
return reply(data);
});
}
}();
dao/task.js
module.exports = function() {
return {
createTask: function createTask(payload, callback) {
//.. Something here which creates the variables err and myData ...
if (err) {
return callback(err);
}
//... If successful ...
callback(null, myData);
}
}();
I think the cool kids now use a package to caught unhandled errors with Hapi, I present to you, Poop.
The only thing Poop is missing is some rich documentation, but check it out, and you'll see that Poop is great.
Some of my friends went to a node.js event in Lisbon, on of the hosts was a guy in charge of web technology stack at Wallmart, they use Hapi.js, Poop and some other cool things.
So if they use poop it must be pretty awesome.
PS: The name is suppa awesome

Categories

Resources