kontist / kontist.github.io Goto Github PK
View Code? Open in Web Editor NEWDeveloper Portal
Home Page: https://kontist.dev
Developer Portal
Home Page: https://kontist.dev
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?
Aktuell kommt man leider nicht auf eure GraphQL-Dokumentation, weil das SSL-Zertifikat anscheinend ausgelaufen ist.
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.
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
}
....
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
}
}
}
...
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:
kontist.service.ts
within my project.port 3000
and have set up ngrok
to expose it externally.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:
@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),
});
}
}
@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 {}
type Transaction {
id: ID!
amount: Int!
bookingDate: String!
purpose: String!
createdAt: String!
}
type Query {
transactions: [Transaction!]!
}
type Subscription {
newTransaction: Transaction!
}
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');
}
}
@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;
}
}
I followed your instructions to the letter:
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}
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.
Hallo!
Bei dem Versuch mein Kontist-Konto mittels API zu erreichen stellte ich heute bei der Authentifizierung nachfolgenden Fehler fest:
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.
{
"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!
The dev docs do not specify which grant types are supported. Is it only Authorization Code?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.