Socket
Socket
Sign inDemoInstall

@crowdin/app-project-module

Package Overview
Dependencies
188
Maintainers
3
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

    @crowdin/app-project-module

Module that generates for you all common endpoints for serving standalone Crowdin App


Version published
Maintainers
3
Created

Readme

Source

Crowdin App Project Module

Module that will automatically add all necessary endpoints for Crowdin App.

It either can extends your Express application or even create it for you.

Module expose two main methods:

  • createApp to fully create for you Express application
  • addCrowdinEndpoints to extend your Express application

In both options you will need to provide Crowdin App configuration file. Please refer to jsdoc for more details.

Status

npm Build Status npm

Table of Contents

Installation

npm

npm i @crowdin/app-project-module

yarn

yarn add @crowdin/app-project-module

Sample app

const crowdinModule = require('@crowdin/app-project-module');
const crowdinAppFunctions = require('@crowdin/crowdin-apps-functions');
const axios = require('axios').default;

const configuration = {
    baseUrl: 'https://123.ngrok.io',
    clientId: 'clientId',
    clientSecret: 'clientSecret',
    name: 'Sample App',
    identifier: 'sample-app',
    description: 'Sample App description',
    dbFolder: __dirname,
    imagePath: __dirname + '/' + 'logo.png',
    integration: {
        withRootFolder: true,
        getIntegrationFiles: async (credentials, appSettings) => {
            //here you need to fetch files/objects from integration
            return [
                {
                    id: '12',
                    name: 'File from integration',
                    type: 'json',
                    parentId: '10'
                },
                {
                    id: '14',
                    name: 'File from integration2',
                    type: 'xml',
                    parentId: '11'
                },
                {
                    id: '10',
                    name: 'Folder from integration'
                },
                {
                    id: '11',
                    name: 'Folder from integratio2'
                },
            ];
        },
        updateCrowdin: async (projectId, client, credentials, request, rootFolder, appSettings) => {
            //here you need to get data from integration and upload it to Crowdin
            console.log(`Request for updating data in Crowdin ${JSON.stringify(request)}`);
            const directories = await client.sourceFilesApi
                .withFetchAll()
                .listProjectDirectories(projectId);
            const { folder, files } = await crowdinAppFunctions.getOrCreateFolder(
                directories.data.map((d) => d.data),
                client,
                projectId,
                'Folder from integration',
                rootFolder
            );
            const fileContent = {
                title: 'Hello World',
            };
            await crowdinAppFunctions.updateOrCreateFile(
                client,
                projectId,
                'integration.json',
                'Sample file from integration',
                'json',
                folder.id,
                fileContent,
                files.find((f) => f.name === 'integration.json'),
            );
        },
        updateIntegration: async (projectId, client, credentials, request, rootFolder, appSettings) => {
            ////here should be logic to get translations from Crowdin and upload them to integration
            console.log(`Request for updating data in Integration ${JSON.stringify(request)}`);
            const directories = await client.sourceFilesApi
                .withFetchAll()
                .listProjectDirectories(projectId);
            const { files } = await crowdinAppFunctions.getFolder(
                directories.data.map((d) => d.data),
                client,
                projectId,
                'Folder from integration',
                rootFolder
            );
            const file = files.find((f) => f.name === 'integration.json');
            if (file) {
                const translationsLink =
                    await client.translationsApi.buildProjectFileTranslation(
                        projectId,
                        file.id,
                        { targetLanguageId: 'uk' },
                    );
                if (!translationsLink) {
                    return;
                }
                const response = await axios.get(translationsLink.data.url);
                console.log(response.data);
            }
        },
    }
};

crowdinModule.createApp(configuration);

Customize your app login form

By default login page for your app will require only to enter apiToken to communicate with third party service.
But there is also a possibility to customize it.

configuration.loginForm = {
    fields: [
        {
            key: 'username',
            label: 'Username',
        },
        {
            key: 'password',
            label: 'Password',
            type: 'password'
        },
        {
            helpText: 'Api Key for http requests',
            key: 'apiKey',
            label: 'Api Key'
        }
    ]
};

OAuth2 support

In case if third party service uses OAuth2 for authorization use oauthLogin field to configure it.
loginForm in this case should remain undefined.

Github example:

configuration.oauthLogin = {
    authorizationUrl: 'https://github.com/login/oauth/authorize',
    clientId: 'github_app_client_id',
    clientSecret: 'github_app_client_secret',
    accessTokenUrl: 'https://github.com/login/oauth/access_token'
}

Google example:

configuration.oauthLogin = {
    scope: 'https%3A//www.googleapis.com/auth/userinfo.email',
    authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
    clientId: 'google_web_app_client_id',
    clientSecret: 'google_web_app_client_secret',
    accessTokenUrl: 'https://oauth2.googleapis.com/token',
    extraAutorizationUrlParameters: {
        response_type: 'code',
        access_type: 'offline',
        prompt: 'consent'
    },
    extraAccessTokenParameters: {
        grant_type: 'authorization_code'
    },
    extraRefreshTokenParameters: {
        grant_type: 'refresh_token'
    },
    refresh: true
}

The oauthLogin property allows you to customize many different properties, mappings, etc. So that you can integrate with any OAuth2 implementation.
Main default values:

  • redirect uri prefix will be /oauth/code
  • client id field name in url parameters and in request payload will be client_id
  • client secret field name in request payload will be client_secret
  • access token field name should be access_token
  • be default assumption is that token do not have any expiration date, to change this behavior use refresh flag so then refresh token and expires in will be taken into consideration
  • access token field name should be refresh_token
  • expires in field name should be expires_in (value should be in seconds)

This module rely that OAuth2 protocol is implemented by third party service in this way:

  • request for access token should be done via POST request to accessTokenUrl with JSON body that will contain at least clientId, clientSecret, code and redirectUri (also possible to add extra fields via extraAccessTokenParameters property)
  • request to refresh token should be done via POST request to accessTokenUrl (or refreshTokenUrl if definied) with JSON body that will contain at least clientId, clientSecret and refreshToken (also possible to add extra fields via extraRefreshTokenParameters property)
  • both requests will return JSON response with body that contains accessToken and, if enabled, refreshToken (optional) and expireIn

To override those requests please use performGetTokenRequest and performRefreshTokenRequest (e.g. when requests should be done with different HTTP methods or data should be tranfered as query string or form data).

Mailup example:

const clientId = 'client_id';
const clientSecret = 'client_secret';
const tokenUrl = 'https://services.mailup.com/Authorization/OAuth/Token';

configuration.oauthLogin = {
    authorizationUrl: 'https://services.mailup.com/Authorization/OAuth/LogOn',
    clientId,
    clientSecret,
    extraAutorizationUrlParameters: {
        response_type: 'code'
    },
    refresh: true,
    performGetTokenRequest: async (code) => {
        const url = `${tokenUrl}?code=${code}&grant_type=authorization_code`;
        const headers = {
            'Authorization': `Bearer ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`
        };
        return (await axios.get(url, { headers })).data;
    },
    performRefreshTokenRequest: async (credentials) => {
        const params = {
            refresh_token: credentials.refreshToken,
            grant_type: 'refresh_token',
            client_id: clientId,
            client_secret: clientSecret
        };
        const data = Object.keys(params)
            .map((key) => `${key}=${encodeURIComponent(params[key])}`)
            .join('&');
        const headers = {
            'Content-Type': 'application/x-www-form-urlencoded'
        };
        return (await axios.post(tokenUrl, data, { headers })).data;
    }
}

Please refer to jsdoc for more details.

Settings window

It is also possible to define settings window for your app where users can customize integration flow.

configuration.integration.getConfiguration = (projectId, crowdinClient, integrationCredentials) => {
    return [
        {
            key: 'flag',
            label: 'Checkbox',
            type: 'checkbox'
        },
        {
            key: 'text',
            label: 'Text input',
            type: 'text',
            helpText: 'Help text'
        },
        {
            key: 'option',
            label: 'Select',
            type: 'select',
            options: [
                {
                    value: '12',
                    label: 'Option'
                }
            ]
        }
    ]
}

Info window

You also can define section with some information notes or help section for your app.

configuration.integration.infoModal = {
    title: 'Info',
    content: `
        <h1>This is your app help section</h1>
        </br>
        <h2>This is just an example</h2>
    `
}

Background tasks

In order to register background tasks that app will invoke periodically invoke you can use cronJobs field.

configuration.integration.cronJobs = [
    {
        //every 10 seconds
        expression: '*/10 * * * * *',
        task: (projectId, client, apiCredentials, appRootFolder, config) => {
            console.log(`Running background task for project : ${projectId}`);
            console.log(`Api credentials : ${JSON.stringify(apiCredentials)}`);
            console.log(`App config : ${JSON.stringify(config)}`);
            console.log(appRootFolder ? JSON.stringify(appRootFolder) : 'No root folder');
        }
    }
]

For cron syntax guide please refer to this documentation.

Error propagation

In case if something is wrong with app settings or credentials are invalid you can throw an explanation message that will be then visible on the UI side.
e.g. check if entered credentials are valid:

configuration.integration.checkConnection = (credentials) => {
    if (!credentials.password  || credentials.password.length < 6) {
        throw 'Password is too weak';
    }
    //or call an service API with those credentials and check if request will be successful
};

Or if you need to manually control users liveness session you can throw an error with 401 code then your app will automatically do a log out action.
e.g. when your service has some specific session duration timeout or extra conditions which are not covered by this framework

configuration.integrartion.getIntegrationFiles = async (credentials, appSettings) => {
    //do a request/custom logic here
    const sessionStillValid = false;
    if (!sessionStillValid) {
        throw {
             message: 'session expired',
             code: 401
        }
    }
    //business logic
}

Contributing

If you want to contribute please read the Contributing guidelines.

Seeking Assistance

If you find any problems or would like to suggest a feature, please feel free to file an issue on Github at Issues Page.

If you've found an error in these samples, please Contact Customer Success Service.

License

The Crowdin App Project module is licensed under the MIT License.
See the LICENSE.md file distributed with this work for additional
information regarding copyright ownership.

Except as contained in the LICENSE file, the name(s) of the above copyright
holders shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written authorization.

Keywords

FAQs

Last updated on 19 Dec 2021

Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc