Auto-generate swagger docs if you only use auto-discovery to discover DB tables - swagger

I am wondering if we can still auto-generate Swagger API documentation for our Loopback API server if we only use the auto-discovery features outlined here:
https://docs.strongloop.com/display/public/LB/Discovering+models+from+relational+databases
does anyone know if it's possible? If we use autodiscovery, I somehow doubt that any .json files for our models will get written to our server project, and that will make generating docs difficult.

Turns out yes it is possible, and the way to do that is to write the models-x.json files out for all models with a script, and then start the server after the script has finished!
https://docs.strongloop.com/display/public/LB/Database+discovery+API
this is standard practice for auto-discovery, here is my code that accomplishes this:
const loopback = require('loopback');
const fs = require('fs');
const path = require('path');
const async = require('async');
var ds = loopback.createDataSource('postgresql', {
'host': 'localhost',
'port': 5432,
'database': 'foo',
'username': 'bar',
'password': 'baz'
});
ds.discoverModelDefinitions(function (err, models) {
async.each(models, function (def, cb) {
ds.discoverSchema(def.name, null, function (err, schema) {
if (err) {
console.error(err.stack || err);
cb(err);
}
else {
fs.writeFile(path.resolve(__dirname, 'server/models', def.name + '.json'),
JSON.stringify(schema), {}, cb);
}
});
}, function (err) {
if (err) {
console.log(err.stack || err);
process.exit(1);
}
else {
console.log(' => Successfully wrote model data.');
process.exit(0);
}
});
});

Related

How to add to WebSocket response some additional information?

I am using Dart Alfred framework. And learning websockets.
Here is implamentation from example:
var users = <WebSocket>[];
app.get('/ws', (req, res) {
return WebSocketSession(
onOpen: (ws) {
users.add(ws);
users
.where((user) => user != ws)
.forEach((user) => user.send('A new user joined the chat.'));
},
onClose: (ws) {
users.remove(ws);
users.forEach((user) => user.send('A user has left.'));
},
onMessage: (ws, dynamic data) async {
users.forEach((user) => user.send(data));
},
);
});
https://github.com/rknell/alfred#websockets
I can't figure out how to return some additional data for every user to client.
For example (let's simplify) it's country from server. For example I have next map:
Map cuntries = {
'Mike': 'USA',
'Piter': 'Holland',
'Jow': 'Italy'
};
I did not worked with WebSocket before. Could anybody provide example how to do it?

Testing graphql subscriptions with k6

Is it possible to test graphql subscriptions using k6 framework?
I tried to do it, but did not have much success. Also tried to do it with k6 websockets, but did not help.
Thanks
Grapqhql Subscription is based on Websockets so this is theoretically possible to implement using k6 WebSocket.
You can also refer to the documentation for subscriptions here.
You can also use the playground and Networks tab in developer tools to figure out the messages/requests that are sent to the server.
Here is how I was able to achieve it:
import ws from "k6/ws";
export default function(){
const url = "ws://localhost:4000/graphql" // replace with your url
const token = null; // replace with your auth token
const operation = `
subscription PostFeed {
postCreated {
author
comment
}
}` // replace with your subscription
const headers = {
"Sec-WebSocket-Protocol": "graphql-ws",
};
if (token != null) Object.assign(headers,{ Authorization: `Bearer ${token}`});
ws.connect(
url,
{
headers,
},
(socket) => {
socket.on("message", (msg) => {
const message = JSON.parse(msg);
if (message.type == "connection_ack")
console.log("Connection Established with WebSocket");
if (message.type == "data") console.log(`Message Received: ${message}`)
});
socket.on("open", () => {
socket.send(
JSON.stringify({
type: "connection_init",
payload: headers,
})
);
socket.send(
JSON.stringify({
type: "start",
payload: {
query: operation,
},
})
);
});
}
);
}
Hope this helps! 🍻

How to setup custom middleware in strapi?

I just wanted to setup a simple custom middleware in strapi. I have tried what they are writing in docs but I found that environments folder and inside configurations are removed. Follwing that currently I have writtent.
/config/environments/development/middleware.json
{
"subscribers": {
"enabled": true
}
}
/config/middleware.json
{
"timeout": 100,
"load": {
"before": ["responseTime", "logger", "cors", "responses", "gzip"],
"order": ["parser", "subscribers"],
"after": ["router"]
}
}
/middlewares/subscribers/index.js
module.exports = (strapi) => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
console.log("I have been called!");
await next();
});
},
};
};
Please help me to implement a middleware in strapi api.Thanks beforehand.
I just did what is written in the docs and I will do the same in my answer!
Initially I was reading from an older version of documentation which is mentioned by #Derrick Mehaffy. I found the correct docs url and read through its middleware implementation. [LINK TO THE DOCS] (Below explanations are obtained from docs)
------------------------------------------------------------------------------------
Examples: Create your custom middleware. [Path — ./middlewares/timer/index.js]
module.exports = strapi => {
return {
initialize() {
strapi.app.use(async (ctx, next) => {
const start = Date.now();
// I just add custom code that logs `I have been called!`
console.log('I have been called!');
await next();
const delta = Math.ceil(Date.now() - start);
ctx.set('X-Response-Time', delta + 'ms');
});
},
};
};
Enable the middleware in environments settings.
Load a middleware at the very first place - !You can do at the proper order
Path — ./config/middleware.js
module.exports = {
load: {
before: ["timer", "responseTime", "logger", "cors", "responses", "gzip"],
order: ["parser", ],
after: ["router", ],
},
settings: {
timer: {
enabled: true,
},
},
};
Basically I just copied and pasted the answer from docs, but it might be helpful for future use that's I have left the question

Upload in GraphQL with NestJs and ValidationPipe

Im trying to make file upload in my api using this strategy: https://stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload/.
Without the ValidationPipe works, but when i enable ValidationPipe this apresent error on class-transformer:
TypeError: Promise resolver undefined is not a function
at new Promise (<anonymous>)
at TransformOperationExecutor.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:117:32)
at _loop_1 (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:235:45)
at TransformOperationExecutor.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/TransformOperationExecutor.js:260:17)
at ClassTransformer.plainToClass (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/ClassTransformer.js:17:25)
at Object.plainToClass (/Users/victorassis/Workspace/barreiroclub/api/node_modules/class-transformer/index.js:20:29)
at ValidationPipe.transform (/Users/victorassis/Workspace/barreiroclub/api/node_modules/#nestjs/common/pipes/validation.pipe.js:40:39)
at /Users/victorassis/Workspace/barreiroclub/api/node_modules/#nestjs/core/pipes/pipes-consumer.js:15:33
at processTicksAndRejections (internal/process/task_queues.js:97:5)
I searched a lot, but seens like class-transformer is abandoned, and the answers was to not use ValidationPipe with upload.
Someone pass for this and found a solution?
I try to follow the example you posted above and then enable the transformation of class and I got no error as you mentioned. But I met this error before when I was trying to put the wrong type of argument in the resolver.
Below is where I setup my app bootstrap:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: extractOrigins(app.get(ConfigService).get('CORS_ORIGINS')),
});
app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
);
app.use(graphqlUploadExpress());
await app.listen(app.get(ConfigService).get('PORT') ?? 3000);
logScaffoldApp(app);
}
bootstrap();
The resolver code:
#Mutation(() => Boolean, { nullable: true })
async uploadVocabularies(
#Args({
name: 'file',
type: () => GraphQLUpload,
})
{ createReadStream, filename }: FileUpload,
) {
console.log('attachment:', filename);
const stream = createReadStream();
stream.on('data', (chunk: Buffer) => {
console.log(chunk);
});
}
And I did get the error when I try to follow another tutorial and trying to make the argument as a Promise so then the class transformer got the same error:
#Mutation(() => Boolean, { nullable: true })
async uploadVocabularies(
#Args({
name: 'file',
type: () => GraphQLUpload,
})
attachment: Promise<FileUpload>,
) {
const { filename, createReadStream } = await attachment;
console.log('attachment:', filename);
const stream = createReadStream();
stream.on('data', (chunk: Buffer) => {
console.log(chunk);
});
}
I hope this can help you and other people who viewed this post ^^

Why are my service workers not working offline

Everything seems to be right and the files are being cached but it just doesn't work offline. Am I missing something obvious?
the cache.addAll did not want to work with my const FILES_TO_CACHE but do work when I put them in directly. Thus the repeated code.
Here is my service worker file:
const FILES_TO_CACHE = [
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
];
const CACHE_NAME = "static-cache-v2";
const DATA_CACHE_NAME = "data-cache-v1";
// install
self.addEventListener("install", function(evt) {
evt.waitUntil(
caches.open(CACHE_NAME).then(cache => {
console.log("Your files were pre-cached successfully!");
return cache.addAll([
"/",
"/index.html",
"/style.css",
"/db.js",
"/index.js",
"/manifest.webmanifest"
]);
})
);
self.skipWaiting();
});
// activate
self.addEventListener("activate", function(evt) {
console.log("activated");
evt.waitUntil(
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
if (key !== CACHE_NAME && key !== DATA_CACHE_NAME) {
console.log("Removing old cache data", key);
return caches.delete(key);
}
})
).catch(err => console.log(err));
})
);
self.clients.claim();
});
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
evt.respondWith(
caches
.open(FILES_TO_CACHE)
.then(cache => {
return fetch(evt.request)
.then(response => {
// If the response was good, clone it and store it in the cache.
if (response.status === 200) {
cache.put(evt.request.url, response.clone());
}
return response;
})
.catch(err => {
// Network request failed, try to get it from the cache.
return cache.match(evt.request);
});
})
.catch(err => console.log(err))
);
return;
}
});
link in html:
<script>
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/service-worker.js").then(function() {
console.log("Service Worker Registered");
});
}
</script>
I also have my manifest linked in the HTML file.
Thank you in advance for any help you can provide!
If you look at the last line of code here:
// fetch
self.addEventListener("fetch", function(evt) {
console.log("fetched", evt.request.url);
if (evt.request.url.includes("/api/")) {
you see that there's a very simple mistake – your Service Worker is ONLY responding to requests that start with "/api/". If they don't, the SW doesn't touch them. Thus only "/api/" calls work offline (which doesn't make any sense :-), apis being mostly dynamic, right?).
(It is possible that there's another bug in the code of course, but this is a good point to start making changes.)

Resources