How to output a file in i18next via custom plugin? - internationalization

I tried creating a custom plugin to download data from backend using i18next plugin, however, is stuck at data dumping process. I am using NextJS for frontend.
Here is the configuration file for NextJS.
const { i18n } = require('./next-i18next.config.js/index.js')
module.exports = {
reactStrictMode: true,
i18n
}
Here is the next-18next.config.js file content.
const getDefault = ()=>{
return {
parse: data => JSON.parse(data),
addPath: '/locales/{{lng}}/{{ns}}',
}
}
class Backend {
constructor(services, backendOptions, i18nextOptions){
this.init(services,backendOptions,i18nextOptions)
}
init (services,options={},allOptions={}){
this.services = services;
this.options = {...getDefault(),...options};
this.allOptions = allOptions;
}
async read(language, namespace, callback) {
const payloadUrl = this.options.payloadUrl;
this.data = null;
try {
const response = await fetch(payloadUrl);
const payloadData = await response.json();
this.data = payloadData;
}
catch(err){
return callback(err,this.data)
}
this.loadUrl(language,namespace,callback);
}
async loadUrl (language,namespace,callback){
const loadPath = this.options.loadPath;
const url = this.services.interpolator.interpolate(loadPath, { lng: language, ns: namespace});
try {
const response = await fetch(url);
const data = await response.json();
return callback(language,namespace,data)
}
catch(err){
console.log("Error while fetching payload ", err)
callback(err,null)
}
}
save (language, namespace, data) {
console.log(language,namespace,data);
}
create (language, namespace, key, fallbackValue) {
console.log(language,namespace,key,fallbackValue)
}
dumpData(language,namespace,data){
const filePath = this.services.interpolator.interpolate(this.options.addPath, { lng: language, ns: namespace});
// need to find a way to dump the file into the given filePath, (right now
// I cannot find proper way to use fs library)
console.log(filePath, "is the file path")
console.log("data we dumping ", data);
}
}
Backend.type = "backend";
module.exports = Backend;
I am following https://github.com/i18next/i18next-http-backend and have no idea how they are dumping the payload we get from backend to specific folder. I tried doing it manually adding fs library, however we are using NextJS and using fs gives me error Can't resolve 'fs'. How can we dump the data we receive from backend into its required location?

The callback signature is wrong, callback(language,namespace,data) is not correct. The signature should be callback(error, result) => this means you may change it to: callback(null, data)
Additionally, all these are not async functions, plain old callback based functions, like described here: https://www.i18next.com/misc/creating-own-plugins#backend
btw: if you're fetching data from your backend, why do you not simply use i18next-http-backend and adapt the loadPath and or the parse option and/or the request option: https://github.com/i18next/i18next-http-backend#backend-options ?

Related

Getting list with the Elrond's esdt tokens (and balances) from an address (a wallet)

I want to get a list with the Elrond's esdt tokens (and balances) from an address (a wallet). I don't have any example, I tried things such as:
const { address, account } = useGetAccountInfo();
const objAddress = new Address(address);
// const data1 = getAccount(address);
const { network } = useGetNetworkConfig();
const proxy = new ProxyProvider(network.apiAddress);
proxy
.getAddressEsdtList(objAddress)
.then(({ returnData }) => {
console.log(returnData);
})
.catch((err) => {
console.error('Unable to call VM query', err);
});
But in the console I get "undefined".
Thanks a lot!
In the latest erdjs version I don't have any getAddressEsdtList function for the provider; what could work is to extend the network providers.
So we know we can extend the available network providers with other functions and we now need to make a request with an address and receive all the tokens that the address helds. api.elrond.com has an endpoint that does this at accounts/{address}/tokens.
As we have an endpoint to make a request to, we can now extend the ApiNetworkProvider, making use of doGetGeneric.
// CustomNetworkProvider.js
import { ApiNetworkProvider } from "#elrondnetwork/erdjs-network-providers";
export class CustomNetworkProvider extends ApiNetworkProvider {
async getTokens(address) {
return await this.doGetGeneric(`accounts/${address}/tokens`);
}
}
// index.js
import {CustomNetworkProvider} from "./CustomNetworkProvider.js";
const getProvider = () => {
return new CustomNetworkProvider('https://api.elrond.com', { timeout: 5000 });
}
const provider = getProvider();
const address = 'erd1rf4hv70arudgzus0ymnnsnc4pml0jkywg2xjvzslg0mz4nn2tg7q7k0t6p';
const tokens = await provider.getTokens(address);
console.log(tokens);

How do I map the root without overwriting other paths with koa-mount?

I have the following structure...
- static
- dashboard.html
But when I use the following...
mainApp.use(mount('/', async (ctx)=>{
...
ctx.body = await pug.render('index', {
user: userInfo
}, true);
}));
mainApp.use(mount('/static', serve(__dirname+"/static")));
But when I run it returns the pug render and not the html file in the static folder. If I comment out...
// mainApp.use(mount('/', async (ctx)=>{
// ...
// ctx.body = await pug.render('index', {
// user: userInfo
// }, true);
// }));
mainApp.use(mount('/static', serve(__dirname+"/static")));
I can get the static file but now there is nothing rendering at root.
you are using template engine the wrong way; use koa-views:
const views = require('koa-views');
const router = require('#koa/router')();
const Koa = require('koa');
const app = new Koa();
// middleware
app.use(views('./views'), {
map: { html: 'pug' }
}));
// route definitions
router.get('/', list)
app.use(router.routes());
async function list(ctx) {
await ctx.render('list', { posts: posts });
}

Is it possible to get the raw body on a custom made endpoint on strapi?

I'm building a custom endpoint on Strapi. For this endpoint, I need to have the raw body content. Is it possible to obtain it from the ctx variable?
stripe : async(ctx) => {
// Handle the event
const sig = ctx.request.headers['stripe-signature']
let event = null
try {
// ctx.request.body needs to be the original raw body
event = stripe.webhooks.constructEvent(ctx.request.body,sig, endpointSecret)
}catch (e) {
ctx.badRequest(null,e)
return
}
Create a middleware (/config/middleware.js) and update it to the following
module.exports = {
settings: {
cors: {
enabled: true,
},
parser: {
enabled: true,
multipart: true,
includeUnparsed: true,
},
},
};
In the controller (/api/<model>/controllers/<model>.js):
const unparsed = require("koa-body/unparsed.js");
const unparsedBody = ctx.request.body[unparsed];
The official koa-bodyparser package actually does this out of the box. See: https://github.com/koajs/bodyparser#raw-body
Here is a small example:
import Koa from 'koa';
import KoaRouter from '#koa/router';
import koaBodyParser from 'koa-bodyparser';
const app = new Koa();
const router = new KoaRouter();
const stripeCheckout = (ctx, next) => {
const sig = ctx.request.header['stripe-signature'];
let event;
if (!process.env.STRIPE_ENDPOINT_SECRET) {
throw new Error('Missing Stripe endpoint secret.');
}
try {
event = stripe.webhooks.constructEvent(
ctx.request.rawBody,
sig,
endpointSecret: process.env.STRIPE_ENDPOINT_SECRET
);
} catch (err) {
logger('error', err);
ctx.status = 400;
ctx.body = `Webhook Error: ${err.message}`;
return next();
}
// ... do something with the event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// ... do something with the checkout session
}
// return a response to acknowledge receipt of the event
ctx.status = 200;
ctx.body = { received: true };
return next();
};
// POST
router.post('/stripe-checkout', stripeCheckout);
app.use(koaBodyParser());
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(port, () => {
logger('log', `✅ Done! Server is listening on http://localhost:${port}`);
});
I'm not sure to understand your needs.
ctx.request.body contains the original body of your request.
After that if you want to send event as response body you can do this like that.
ctx.body = event;
And a warning in your code. You wrote define a const for event and you assign event withe the result of your strapi webhook. You have to define a let variable.
It actually works by switching on "includingUnparsed" in the request environment configuration (config/environments/development/request.json -> parser.includedUnparsed: true).
You can access the unparsed body using the koa-body built-in feature for that:
Some applications require crytopgraphic verification of request bodies, for example webhooks from slack or stripe. The unparsed body can be accessed if includeUnparsed is true in koa-body's options. When enabled, import the symbol for accessing the request body from unparsed = require('koa-body/unparsed.js'), or define your own accessor using unparsed = Symbol.for('unparsedBody'). Then the unparsed body is available using ctx.request.body[unparsed].
koa-body docs

ms-rest-nodeauth (azure-sdk-for-js) : Error: credentials argument needs to implement signRequest method

Trying to follow the samples from https://github.com/Azure/ms-rest-nodeauth
When passing authresponse to a client to generate a client to ping resources, I end up getting:
Error: credentials argument needs to implement signRequest method
I am trying to read through the documents to see if I need to sign the token's I am getting back from the SDK/Azure AD, but the documentation for the new SDK doesnt show anything
Figured it out, have to call .credentials on the authresponse
Adding the code, using #azure/arm-billing, in case the full code file is helpful.
// auth.json
// Create auth file with Azure CLI:`az ad sp create-for-rbac --sdk-auth > auth.json`
{
"clientId": "",
"clientSecret": "",
"subscriptionId": "",
"tenantId": "",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
// index.js
const msRest = require("#azure/ms-rest-js");
const msRestAzure = require("#azure/ms-rest-azure-js");
const msRestNodeAuth = require("#azure/ms-rest-nodeauth");
const armBilling = require("#azure/arm-billing");
const path = require('path');
// list billing information
const lists = async (client) => {
try {
let lists = [];
const enrollmentAccounts = await client.enrollmentAccounts.list();
lists.push(enrollmentAccounts);
const billingPeriods = await client.billingPeriods.list();
lists.push(billingPeriods);
const invoices = await client.invoices.list();
lists.push(invoices);
return lists;
} catch (err) {
console.log(err);
throw (err);
}
}
// sample auth file created from Azure CLI - removed PII
const authenticationFile = path.join(__dirname, "./auth.json");
const options = {
filePath: authenticationFile
};
// get subscriptionId from auth file
const subscriptionIdFromAuthFile = require('./auth.json').subscriptionId;
// authenticate and getting billing information
msRestNodeAuth.loginWithAuthFileWithAuthResponse(options).then(async (response) => {
console.log("authenticated");
// --- CHANGE response parameter to -> response.credentials
const client = new armBilling.BillingManagementClient(response.credentials, subscriptionIdFromAuthFile);
console.log("client created");
const results = await lists(client);
console.log(`The result is:${JSON.stringify(results)}`);
}).catch((err) => {
console.error(err);
});

Apollo Server - Apply Authentication to Certain Resolvers Only with Passport-JWT

I currently have a Node.js back-end running Express with Passport.js for authentication and am attempting to switch to GraphQL with Apollo Server. My goal is to implement the same authentication I am using currently, but cannot figure out how to leave certain resolvers public while enabling authorization for others. (I have tried researching this question extensively yet have not been able to find a suitable solution thus far.)
Here is my code as it currently stands:
My JWT Strategy:
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = JWT_SECRET;
module.exports = passport => {
passport.use(
new JwtStrategy(opts, async (payload, done) => {
try {
const user = await UserModel.findById(payload.sub);
if (!user) {
return done(null, false, { message: "User does not exist!" });
}
done(null, user);
} catch (error) {
done(err, false);
}
})
);
}
My server.js and Apollo configuration:
(I am currently extracting the bearer token from the HTTP headers and passing it along to my resolvers using the context object):
const apollo = new ApolloServer({
typeDefs,
resolvers,
context: async ({ req }) => {
let authToken = "";
try {
if (req.headers.authorization) {
authToken = req.headers.authorization.split(" ")[1];
}
} catch (e) {
console.error("Could not fetch user info", e);
}
return {
authToken
};
}
});
apollo.applyMiddleware({ app });
And finally, my resolvers:
exports.resolvers = {
Query: {
hello() {
return "Hello world!";
},
async getUserInfo(root, args, context) {
try {
const { id } = args;
let user = await UserModel.findById(id);
return user;
} catch (error) {
return "null";
}
},
async events() {
try {
const eventsList = await EventModel.find({});
return eventsList;
} catch (e) {
return [];
}
}
}
};
My goal is to leave certain queries such as the first one ("hello") public while restricting the others to requests with valid bearer tokens only. However, I am not sure how to implement this authorization in the resolvers using Passport.js and Passport-JWT specifically (it is generally done by adding middleware to certain endpoints, however since I would only have one endpoint (/graphql) in this example, that option would restrict all queries to authenticated users only which is not what I am looking for. I have to perform the authorization in the resolvers somehow, yet not sure how to do this with the tools available in Passport.js.)
Any advice is greatly appreciated!
I would create a schema directive to authorized query on field definition and then use that directive wherever I want to apply authorization. Sample code :
class authDirective extends SchemaDirectiveVisitor {
visitObject(type) {
this.ensureFieldsWrapped(type);
type._requiredAuthRole = this.args.requires;
}
visitFieldDefinition(field, details) {
this.ensureFieldsWrapped(details.objectType);
field._requiredAuthRole = this.args.requires;
}
ensureFieldsWrapped(objectType) {
// Mark the GraphQLObjectType object to avoid re-wrapping:
if (objectType._authFieldsWrapped) return;
objectType._authFieldsWrapped = true;
const fields = objectType.getFields();
Object.keys(fields).forEach(fieldName => {
const field = fields[fieldName];
const {
resolve = defaultFieldResolver
} = field;
field.resolve = async function (...args) {
// your authorization code
return resolve.apply(this, args);
};
});
}
}
And declare this in type definition
directive #authorization(requires: String) on OBJECT | FIELD_DEFINITION
map schema directive in your schema
....
resolvers,
schemaDirectives: {
authorization: authDirective
}
Then use it on your api end point or any object
Query: {
hello { ... }
getuserInfo():Result #authorization(requires:authToken) {...}
events():EventResult #authorization(requires:authToken) {...}
};

Resources