Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial commit for basic gateway package #1619

Merged
merged 6 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/build-gateway-lib.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build JS libs

on:
pull_request:
paths:
- '.github/'
- 'tools/gateway-js/'
branches:
- main

jobs:
build-and-publish:
runs-on: ubuntu-latest

steps:
# Check out the repository
- name: Checkout Repository
uses: actions/checkout@v3

# Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18'

# Install dependencies
- name: Install Dependencies
run: npm install

# Build the project (assuming you have a script named "build" in package.json)
- name: Build
run: npm run lint && npm run test

# Deploy to GitHub Pages using the 'gh-pages' branch
- name: Deploy to GitHub Pages
uses: JamesIves/[email protected]
with:
branch: gh-pages
folder: dist
39 changes: 39 additions & 0 deletions .github/workflows/deploy-gateway-lib.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Publish JS for External Consumption and GitHub Pages

on:
push:
paths:
- '.github/'
- 'tools/gateway-js/'
branches:
- main

jobs:
build-and-publish:
runs-on: ubuntu-latest

steps:
# Check out the repository
- name: Checkout Repository
uses: actions/checkout@v3

# Set up Node.js
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '18'

# Install dependencies
- name: Install Dependencies
run: npm install

# Build the project (assuming you have a script named "build" in package.json)
- name: Build
run: npm run build

# Deploy to GitHub Pages using the 'gh-pages' branch
- name: Deploy to GitHub Pages
uses: JamesIves/[email protected]
with:
branch: gh-pages
folder: dist
34 changes: 34 additions & 0 deletions tools/gateway-js/gateway-lib/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true,
"jest": true,
},
"extends": "eslint:recommended",
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"ignorePatterns": [
"node_modules/",
"dist/",
"*.config.*"
],
"rules": {
"no-unused-vars": "off"
}
}
58 changes: 58 additions & 0 deletions tools/gateway-js/gateway-lib/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Obscuro Gateway JS

A JavaScript library for the gateway, providing streamlined access and functionalities for interacting with the Obscuro network.

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Usage](#usage)
- [Build](#build)
- [Contribute](#contribute)
- [License](#license)

## Features

- Seamless connection to Obscuro Network.
- Easy-to-use methods for joining and authenticating.
- External consumption support through CDN or NPM.

## Installation

To install `obscuro-gateway-js`, use npm:

\`\`\`bash
npm install obscuro-gateway-js
\`\`\`

## Usage

\`\`\`javascript

const Gateway = require('obscuro-gateway-js');

const gateway = new Gateway(httpURL, wsURL, provider);
await gateway.join();
await gateway.registerAccount(account);

\`\`\`

## Build

To build for development:

\`\`\`bash
npm run dev
\`\`\`

For production:

\`\`\`bash
npm run build
\`\`\`

The production build will be available for external consumption on GitHub Pages at `https://go-obscuro.github.io/obscuronet/gateway.bundle.js`.

## Contribute

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
1 change: 1 addition & 0 deletions tools/gateway-js/gateway-lib/dist/gateway.bundle.js

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions tools/gateway-js/gateway-lib/gateway.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const axios = require("axios");

const obscuroGatewayVersion = "v1";
const pathJoin = obscuroGatewayVersion + "/join/";
const pathAuthenticate = obscuroGatewayVersion + "/authenticate/";

otherview marked this conversation as resolved.
Show resolved Hide resolved
class Gateway {
constructor(httpURL, wsURL, provider) {
this.httpURL = httpURL;
this.wsURL = wsURL;
this.userId = '';
this.provider = provider;
}

async join() {
try {
const response = await axios.get(`${this.httpURL}${pathJoin}`);
if (response.status !== 200) {
throw new Error(`Failed to get userID. Status code: ${response.status}`);
}
// todo make further checks on the data
this.userId = response.data;
} catch (error) {
throw new Error(`Failed to get userID. ${error}`);
}
}
otherview marked this conversation as resolved.
Show resolved Hide resolved

async registerAccount(address) {
const message = `Register ${this.userID} for ${address.toLowerCase()}`;
let signature = ""

try {
signature = await this.provider.request({
method: "personal_sign",
params: [message, address]
})
} catch (err) {
throw new Error(`Failed to sign message. ${err}`);
}

// todo make further checks on the data
if (signature === -1) {
return "Signing failed"
}
otherview marked this conversation as resolved.
Show resolved Hide resolved

try {
const authenticateUserURL = pathAuthenticate+"?u="+this.userId
const authenticateFields = {"signature": signature, "message": message}
const authenticateResp = await axios.post(
authenticateUserURL,
authenticateFields,
{
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
}
}
);
// todo make further checks on the data
return await authenticateResp.text()
} catch (error) {
throw new Error(`Failed to register account. ${error}`);
}
Comment on lines +28 to +63
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The registerAccount method constructs a message and signs it using the provider, but it does not handle the case where the provider fails to sign the message. This could lead to unexpected behavior or errors later on. Consider adding a check to ensure that signature is not null or empty before proceeding with the authentication request.

}

userID() {
return this.userId;
}

http() {
return `${this.httpURL}/v1/?u=${this.userId}`;
}

ws() {
return `${this.wsURL}/v1/?u=${this.userId}`;
}
Comment on lines +66 to +76
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The userID, http, and ws methods return values derived from this.userId, but do not handle the case where this.userId is null or empty. This could lead to unexpected behavior or errors later on. Consider adding checks to ensure that this.userId is not null or empty before returning these values.

const axios = require("axios");

class Gateway {
    constructor(httpURL, wsURL, provider, obscuroGatewayVersion = "v1") {
        this.httpURL = httpURL;
        this.wsURL = wsURL;
        this.userId = '';
        this.provider = provider;
        this.pathJoin = obscuroGatewayVersion + "/join/";
       this.pathAuthenticate = obscuroGatewayVersion + "/authenticate/";
   }

   async join() {
       try {
           const response = await axios.get(`${this.httpURL}${this.pathJoin}`);
           if (response.status !== 200 || !response.data) {
               throw new Error(`Failed to get userID. Status code: ${response.status}`);
           }
           // todo make further checks on the data
           this.userId = response.data;
       } catch (error) {
           throw new Error(`Failed to get userID. ${error}`);
       }
   }

   async registerAccount(address) {
       const message = `Register ${this.userID} for ${address.toLowerCase()}`;
       let signature = ""

       try {
               signature = await this.provider.request({
                   method: "personal_sign",
                   params: [message, address]
               })
           } catch (err) {
               throw new Error(`Failed to sign message. ${err}`);
           }

       // todo make further checks on the data
       if (signature === -1 || !signature) {
           return "Signing failed"
       }

       try {
           const authenticateUserURL = this.pathAuthenticate+"?u="+this.userId
           const authenticateFields = {"signature": signature, "message": message}
           const authenticateResp = await axios.post(
               authenticateUserURL,
               authenticateFields,
               {
                   headers: {
                       "Accept": "application/json",
                       "Content-Type": "application/json"
                   }
               }
           );
           // todo make further checks on the data
           return await authenticateResp.text()
       } catch (error) {
           throw new Error(`Failed to register account. ${error}`);
       }
   }

   userID() {
       if (!this.userId) {
           throw new Error("User ID is not set.");
       }
       return this.userId;
   }

   http() {
       if (!this.userId) {
           throw new Error("User ID is not set.");
       }
       return `${this.httpURL}/v1/?u=${this.userId}`;
   }

   ws() {
       if (!this.userId) {
           throw new Error("User ID is not set.");
       }
       return `${this.wsURL}/v1/?u=${this.userId}`;
   }
}

module.exports = Gateway;
Committable suggestion (Beta)
Suggested change
userID() {
return this.userId;
}
http() {
return `${this.httpURL}/v1/?u=${this.userId}`;
}
ws() {
return `${this.wsURL}/v1/?u=${this.userId}`;
}
const axios = require("axios");
class Gateway {
constructor
<!-- This is an auto-generated comment by CodeRabbit -->

}

module.exports = Gateway;
59 changes: 59 additions & 0 deletions tools/gateway-js/gateway-lib/gateway.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const axios = require('axios');
const Gateway = require('./gateway.js');

// Mocking axios module
jest.mock('axios');

// Mocking the global fetch function
global.fetch = jest.fn();

describe('Gateway', () => {
let gateway;
const httpURL = 'http://example.com';
const wsURL = 'ws://example.com';
const provider = {}; // Placeholder for the provider if it's needed in future tests.

beforeEach(() => {
gateway = new Gateway(httpURL, wsURL, provider);
});
otherview marked this conversation as resolved.
Show resolved Hide resolved

it('should join successfully', async () => {
axios.get.mockResolvedValue({
status: 200,
data: 'testUserID',
});

await gateway.join();

expect(gateway.userId).toBe('testUserID');
});

it('should throw error on unsuccessful join', async () => {
axios.get.mockRejectedValue(new Error('Network error'));

await expect(gateway.join()).rejects.toThrow('Failed to get userID. Error: Network error');
});

it('should register account successfully', async () => {
axios.post.mockResolvedValue({
status: 200,
text: jest.fn().mockResolvedValue('Account registered')
});
otherview marked this conversation as resolved.
Show resolved Hide resolved

gateway = new Gateway(httpURL, wsURL, {
request: jest.fn().mockResolvedValue("mockSignature")
})

const result = await gateway.registerAccount( 'address');

expect(result).toBe('Account registered');
});

it('should throw error on unsuccessful account registration', async () => {
gateway = new Gateway(httpURL, wsURL, {
request: jest.fn().mockRejectedValue(new Error('Signature error')),
})

await expect(gateway.registerAccount('address')).rejects.toThrow('Failed to sign message. Error: Signature error');
});
});
Loading
Loading