GithubHelp home page GithubHelp logo

kontist / kontist.github.io Goto Github PK

View Code? Open in Web Editor NEW
3.0 3.0 0.0 6.17 MB

Developer Portal

Home Page: https://kontist.dev

HTML 35.77% Ruby 1.07% CSS 2.62% JavaScript 4.41% TypeScript 41.67% Shell 0.20% SCSS 14.25%

kontist.github.io's People

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

kontist.github.io's Issues

KontistSDKError: Too Many Requests - Authentication Issues in Local Server

Hello! I hope you're doing well.

I'm currently working on integrating authentication with the api.kontist.com server, following the kontist.com documentation. Everything works fine on my local server until I turn it off. My setup involves connecting to the api.kontist.com server and fetching a transaction list every 5 seconds.
However, after about 10 to 15 minutes, I encounter a problem where I can no longer obtain an accessToken, and I have to reconnect to api.kontist.com. After attempting to reconnect multiple times, I end up receiving a "Too Many Requests" error from api.kontist.com, which causes my server to stop due to the numerous errors.

I'll share my current code with you. Could you please take a careful look and advise me on what adjustments I might need to make?

Error message like that:

subscribeNewTransactionByFiveSeconds error KontistSDKError: request to https://api.kontist.com/api/user/mfa/challenges/501427e9-e92b-4973-b94d-d912f85bf984 failed, reason: getaddrinfo ENOTFOUND api.kontist.com
    at new KontistSDKError (<anonymized>/node_modules/kontist/lib/errors.ts:28:5)
    at HttpRequest.<anonymous> (<anonymized>/node_modules/kontist/lib/request.ts:55:13)
    at step (<anonymized>/node_modules/kontist/dist/lib/request.js:33:23)
    at Object.throw (<anonymized>/node_modules/kontist/dist/lib/request.js:14:53)
    at rejected (<anonymized>/node_modules/kontist/dist/lib/request.js:6:65)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  status: undefined,
  type: undefined
}

KontistSDKError: Too Many Requests
    at new KontistSDKError (<anonymized>/node_modules/kontist/lib/errors.ts:28:5)
    at HttpRequest.<anonymous> (<anonymized>/node_modules/kontist/lib/request.ts:55:13)
    at step (<anonymized>/node_modules/kontist/dist/lib/request.js:33:23)
    at Object.next (<anonymized>/node_modules/kontist/dist/lib/request.js:14:53)
    at fulfilled (<anonymized>/node_modules/kontist/dist/lib/request.js:5:58)
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {
  status: 429,
  type: undefined
}

transaction.service.ts

....
constructor(private httpService: HttpService, private transactionsGateway: TransactionsGateway) {
        const keepAliveAgent = new https.Agent({ keepAlive: true });
        this.client = new Client({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ["transactions", "subscriptions"],
            agent: keepAliveAgent,
        });
        this.accessToken = null;
        this.accessTokenExpiresAt = null; // Track the expiry of the token
    }
    username = process.env.USERNAME;
    password = process.env.PASSWORD;

    retryDelay = 180000; // Initial delay in milliseconds for exponential backoff
    maxRetries = 3; // Maximum number of retries


    async waitFor(delay) {
        return new Promise(resolve => setTimeout(resolve, delay));
    }

    async initiateMFA(retryCount = 0) {
        try {
            const tokenData: any = await this.client.auth.tokenManager.fetchTokenFromCredentials({
                username: this.username,
                password: this.password
            });
            this.accessToken = tokenData.accessToken;
            this.accessTokenExpiresAt = new Date(new Date().getTime() + tokenData.expires * 1000);

            const confirmedToken: any = await this.client.auth.push.getConfirmedToken();
            this.accessToken = confirmedToken.accessToken;
            this.accessTokenExpiresAt = new Date(new Date().getTime() + confirmedToken.expires * 1000);

            console.log("MFA confirmed. Access token is now confirmed.");
            this.retryDelay = 180000; // Reset retry delay upon success
        } catch (err) {
            if (err instanceof ChallengeExpiredError) {
                console.log("Challenge expired, trying again...");
                await this.initiateMFA(retryCount + 1); // Retry initiating MFA if the challenge expired
            } else if (err.status === 429 && retryCount < this.maxRetries) { // Check for rate limit error and retry count
                console.log(`Rate limit hit, retrying in ${this.retryDelay}ms...`);
                await this.waitFor(this.retryDelay);
                this.retryDelay *= 2; // Exponentially increase the delay
                await this.initiateMFA(retryCount + 1);
            } else {
                throw err; // Re-throw other errors that we're not handling here
            }
        }
    }

    async getAccessToken() {
        if (!this.accessToken || new Date() >= this.accessTokenExpiresAt) {
            await this.initiateMFA();
        }
        return this.accessToken;
    }

async subscribeNewTransactionByFiveSeconds(retryCount = 0) {
        const maxRetries = 3;
        const baseDelay = 180000; // 3 minutes
        const maxDelay = 600000; // 10 minutes

        try {
            const accessToken = await this.getAccessToken();
            const transactions = await this.client.models.transaction.fetch();
            // Filter transactions
            const fiveSecondsAgo = new Date(new Date().getTime() - 5 * 1000).toISOString();
            const transactionList = transactions.items;
            const searchResult = transactionList.filter(transaction => new Date(transaction.createdAt) > new Date(fiveSecondsAgo));
            if (searchResult.length > 0) {
                this.transactionsGateway.sendNewTransactionToClients(searchResult);
            }
            return searchResult;
        } catch (error) {
            console.error("subscribeNewTransactionByFiveSeconds error", error);
            if (error.status === 429) { // Rate limiting error
                console.log("Rate limiting error, retrying...");
                const delay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);
                console.log(`Retrying after ${delay}ms...`);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        this.subscribeNewTransactionByFiveSeconds(retryCount + 1).then(resolve).catch(reject);
                    }, delay);
                });
            } else if (retryCount < maxRetries) {
                // Retry for other errors
                const delay = baseDelay;
                console.log(`Retrying for error after ${delay}ms...`);
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        this.subscribeNewTransactionByFiveSeconds(retryCount + 1).then(resolve).catch(reject);
                    }, delay);
                });
            } else {
                throw error; // Rethrow after max retries
            }
        }
    }
...

Missing Asset

I am not able to query the asset from my Kontist Invoice. If I add a second one by my self I will only receive that, but not the origin one.

400 - Invalid payload: client

I followed your instructions to the letter:

  1. I created an app with the correct scopes and redirect_uris
  2. I used the exact same authorization code URL as in your instructions: https://api.kontist.com/api/oauth/authorize?scope=offline&response_type=code&client_id={my client ID}&redirect_uri={my redirect URI}&state={a value}
  3. I pasted this URL in my browser.

Resulting URL: https://api.kontist.com/api/oauth/mfa/authorize?allowed=true

Response:

{
  "status": 400,
  "type": "http://www.iana.org/assignments/http-status-codes#400",
  "title": "Invalid payload: client"
}

Could you help me figure out what I've done wrong? The error message gives zero useful information.

Seeking Assistance with Kontist API Integration for Transaction Notifications

Hello! I hope you're doing well.

I'm currently integrating the Kontist API into my local server to manage transactions more efficiently. My goal is to set up a feature where my application can subscribe to notifications of new transactions as they occur within the Kontist system.
Here’s what I’ve done so far:

  • I've created a new client account on Kontist.
  • I've managed to authenticate successfully and can retrieve a list of transactions without issues.
  • I've written the necessary code in a file named kontist.service.ts within my project.
  • To make my local server accessible, I'm running it on port 3000 and have set up ngrok to expose it externally.
  • Despite these steps, I haven't been able to receive any notifications from the Kontist App about new transactions. I suspect the issue might be related to how I'm using ngrok or perhaps something within my kontist.service.ts file.

Could you please review my setup to see if there's anything I might have missed or need to adjust? Any guidance or recommendations would be greatly appreciated.

Thank you so much for your assistance. Here is the code:

kontist.service.ts

@Injectable()
export class KontistService implements OnModuleInit {
    private client: ApolloClient<any>;
    private kontistClient: Client;

    constructor() {
        this.kontistClient = new Client({
            clientId: process.env.CLIENT_ID,
            clientSecret: process.env.CLIENT_SECRET,
            scopes: ["transactions", "subscriptions"]
        });
    }

    onModuleInit() {
        this.setupSubscription();
    }

    setupSubscription() {
        const username = process.env.USERNAME;
        const password = process.env.PASSWORD;
        this.kontistClient.auth.tokenManager.fetchTokenFromCredentials({ username: username, password: password }).then((tokenData) => {
            const accessToken = tokenData.accessToken;
            const wsClient = new SubscriptionClient(
                'wss://api.kontist.com/api/graphql',
                {
                    reconnect: true,
                    connectionParams: {
                        headers: {
                            Authorization: `Bearer ${accessToken}`,
                        },
                    },
                },
                WebSocket,
            );
            const link = new WebSocketLink(wsClient);
            this.client = new ApolloClient({
                link,
                cache: new InMemoryCache(),
            });
            this.subscribeToNewTransactions();
        });
    }

    subscribeToNewTransactions() {
        const SUBSCRIBE_TO_NEW_TRANSACTION = gql`
          subscription {
            newTransaction {
              id
              amount
              bookingDate
            }
          }
        `;

        console.log('Subscribing to new transactions')
        this.client.subscribe({
            query: SUBSCRIBE_TO_NEW_TRANSACTION,
        }).subscribe({
            next: (data) => {
                console.log('New transaction:', data);
                // Here, you can integrate with Prisma to save the transaction or perform any other logic
            },
            error: (err) => console.error('Error on new transaction subscription:', err),
        });
    }
 }

transaction.module.ts

@Module({
    imports: [
        HttpModule,
        GraphQLModule.forRoot<ApolloDriverConfig>({
            driver: ApolloDriver,
            playground: false,
            typePaths: ['./**/*.graphql'],
            installSubscriptionHandlers: true,
            subscriptions: {
                'graphql-ws': true,
            }
        }),
    ],
    providers: [
        TransactionsService,
        TransactionsGateway,
        PrismaService,
        TransactionResolver,
        AuthService,
        JwtService,
        UsersService,
        KontistService,
    ],
    exports: [KontistService, TransactionsService, TransactionsGateway],
    controllers: [TransactionsController],
})
export class TransactionsModule {}

transaction.graphql

type Transaction {
    id: ID!
    amount: Int!
    bookingDate: String!
    purpose: String!
    createdAt: String!
}

type Query {
    transactions: [Transaction!]!
}

type Subscription {
    newTransaction: Transaction!
}

transaction.resolver.ts

import { Resolver, Subscription } from '@nestjs/graphql';
import { PubSub } from 'graphql-subscriptions';
import {Transaction} from "kontist/dist/lib/graphql/transaction";

const pubSub = new PubSub();

@Resolver(of => Transaction)
export class TransactionResolver {
    @Subscription(returns => Transaction)
    newTransaction() {
        return pubSub.asyncIterator('newTransaction');
    }
}

transaction.controller.ts

@Controller('transactions')
@ApiTags('Transactions')
export class TransactionsController {
    constructor(private readonly service: TransactionsService, private readonly kontistService: KontistService) {}

    @Get('getAllList')
    async getAllTransactions() {
        const transactions = this.service.getAllTransactions();
        return transactions;
    }
 }

Test Account

Hi, I'd like to play around with the Kontist API but I don't want to mess with my real account. Would it instead be possible to get some kind of testing/dummy account for development purposes?

400 - Invalid payload: client

Hallo!

Bei dem Versuch mein Kontist-Konto mittels API zu erreichen stellte ich heute bei der Authentifizierung nachfolgenden Fehler fest:

  1. Aufruf von

https://api.kontist.com/api/oauth/authorize?scope=offline&response_type=code&client_id={Client ID meiner zuvor erstellten Anwendung}&redirect_uri=https://meine-domain.tld/callback.php&state=OPAQUE_VALUE

im Browser.

  1. Authentifizierung mittels Anmeldedaten inkl. Bestätigung der Anmeldung in der Kontist-App. Ergebnis:

{
"status": 400,
"type": "http://www.iana.org/assignments/http-status-codes#400",
"title": "Invalid payload: client"
}

Verweigere ich jedoch den Zugriff der Anwendung, so werde ich korrekt zu meiner Application weitergeleitet und kann die GET-Parameter "state" und "error" auslesen und verarbeiten. Die "callback.php" enthält zu Testzwecken nur eine Ausgabe der GET-Parameter.

Mache ich etwas falsch oder habe ich etwas übersehen? Für meinen Test habe ich Mozilla Firefox und Microsoft Edge verwendet.

Vielen Dank für die Unterstützung!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.