Build stripe integration on backend side (#5246)

Adding stripe integration by making the server logic independent of the
input fields:
- query factories (remote server, foreign data wrapper, foreign table)
to loop on fields and values without hardcoding the names of the fields
- adding stripe input and type
- add the logic to handle static schema. Simply creating a big object to
store into the server

Additional work:
- rename username field to user. This is the input intended for postgres
user mapping and we now need a matching by name

---------

Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
Thomas Trompette
2024-05-02 17:13:15 +02:00
committed by GitHub
parent 5128ea3ffb
commit f9c19c839b
30 changed files with 396 additions and 283 deletions

View File

@ -1,4 +1,4 @@
import { ForeignDataWrapperQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/foreign-data-wrapper-query.factory';
import { ForeignDataWrapperServerQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/foreign-data-wrapper-server-query.factory';
import { ArgsAliasFactory } from './args-alias.factory';
import { ArgsStringFactory } from './args-string.factory';
@ -30,5 +30,5 @@ export const workspaceQueryBuilderFactories = [
UpdateOneQueryFactory,
UpdateManyQueryFactory,
DeleteManyQueryFactory,
ForeignDataWrapperQueryFactory,
ForeignDataWrapperServerQueryFactory,
];

View File

@ -1,105 +0,0 @@
import { Injectable } from '@nestjs/common';
import { isDefined } from 'class-validator';
import {
ForeignDataWrapperOptions,
RemoteServerType,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptions } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
@Injectable()
export class ForeignDataWrapperQueryFactory {
createForeignDataWrapper(
foreignDataWrapperId: string,
foreignDataWrapperType: RemoteServerType,
foreignDataWrapperOptions: ForeignDataWrapperOptions<RemoteServerType>,
) {
const [name, options] = this.buildNameAndOptionsFromType(
foreignDataWrapperType,
foreignDataWrapperOptions,
);
return `CREATE SERVER "${foreignDataWrapperId}" FOREIGN DATA WRAPPER ${name} OPTIONS (${options})`;
}
updateForeignDataWrapper({
foreignDataWrapperId,
foreignDataWrapperOptions,
}: {
foreignDataWrapperId: string;
foreignDataWrapperOptions: Partial<
ForeignDataWrapperOptions<RemoteServerType>
>;
}) {
const options = this.buildUpdateOptions(foreignDataWrapperOptions);
return `ALTER SERVER "${foreignDataWrapperId}" OPTIONS (${options})`;
}
createUserMapping(
foreignDataWrapperId: string,
userMappingOptions: UserMappingOptions,
) {
// CURRENT_USER works for now since we are using only one user. But if we switch to a user per workspace, we need to change this.
return `CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER "${foreignDataWrapperId}" OPTIONS (user '${userMappingOptions.username}', password '${userMappingOptions.password}')`;
}
updateUserMapping(
foreignDataWrapperId: string,
userMappingOptions: Partial<UserMappingOptions>,
) {
const options = this.buildUpdateUserMappingOptions(userMappingOptions);
// CURRENT_USER works for now since we are using only one user. But if we switch to a user per workspace, we need to change this.
return `ALTER USER MAPPING FOR CURRENT_USER SERVER "${foreignDataWrapperId}" OPTIONS (${options})`;
}
private buildNameAndOptionsFromType(
type: RemoteServerType,
options: ForeignDataWrapperOptions<RemoteServerType>,
) {
switch (type) {
case RemoteServerType.POSTGRES_FDW:
return ['postgres_fdw', this.buildPostgresFDWQueryOptions(options)];
default:
throw new Error('Foreign data wrapper type not supported');
}
}
private buildUpdateOptions(
options: Partial<ForeignDataWrapperOptions<RemoteServerType>>,
) {
const rawQuerySetStatements: string[] = [];
Object.entries(options).forEach(([key, value]) => {
if (isDefined(value)) {
rawQuerySetStatements.push(`SET ${key} '${value}'`);
}
});
return rawQuerySetStatements.join(', ');
}
private buildUpdateUserMappingOptions(
userMappingOptions?: Partial<UserMappingOptions>,
) {
const setStatements: string[] = [];
if (isDefined(userMappingOptions?.username)) {
setStatements.push(`SET user '${userMappingOptions?.username}'`);
}
if (isDefined(userMappingOptions?.password)) {
setStatements.push(`SET password '${userMappingOptions?.password}'`);
}
return setStatements.join(', ');
}
private buildPostgresFDWQueryOptions(
foreignDataWrapperOptions: ForeignDataWrapperOptions<RemoteServerType>,
) {
return `dbname '${foreignDataWrapperOptions.dbname}', host '${foreignDataWrapperOptions.host}', port '${foreignDataWrapperOptions.port}'`;
}
}

View File

@ -0,0 +1,69 @@
import { Injectable } from '@nestjs/common';
import {
ForeignDataWrapperOptions,
RemoteServerType,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptions } from 'src/engine/metadata-modules/remote-server/types/user-mapping-options';
@Injectable()
export class ForeignDataWrapperServerQueryFactory {
createForeignDataWrapperServer(
foreignDataWrapperId: string,
foreignDataWrapperType: RemoteServerType,
foreignDataWrapperOptions: ForeignDataWrapperOptions<RemoteServerType>,
) {
const options = this.buildQueryOptions(foreignDataWrapperOptions, false);
return `CREATE SERVER "${foreignDataWrapperId}" FOREIGN DATA WRAPPER ${foreignDataWrapperType} OPTIONS (${options})`;
}
updateForeignDataWrapperServer({
foreignDataWrapperId,
foreignDataWrapperOptions,
}: {
foreignDataWrapperId: string;
foreignDataWrapperOptions: Partial<
ForeignDataWrapperOptions<RemoteServerType>
>;
}) {
const options = this.buildQueryOptions(foreignDataWrapperOptions, true);
return `ALTER SERVER "${foreignDataWrapperId}" OPTIONS (${options})`;
}
createUserMapping(
foreignDataWrapperId: string,
userMappingOptions: UserMappingOptions,
) {
const options = this.buildQueryOptions(userMappingOptions, false);
// CURRENT_USER works for now since we are using only one user. But if we switch to a user per workspace, we need to change this.
return `CREATE USER MAPPING IF NOT EXISTS FOR CURRENT_USER SERVER "${foreignDataWrapperId}" OPTIONS (${options})`;
}
updateUserMapping(
foreignDataWrapperId: string,
userMappingOptions: Partial<UserMappingOptions>,
) {
const options = this.buildQueryOptions(userMappingOptions, true);
// CURRENT_USER works for now since we are using only one user. But if we switch to a user per workspace, we need to change this.
return `ALTER USER MAPPING FOR CURRENT_USER SERVER "${foreignDataWrapperId}" OPTIONS (${options})`;
}
private buildQueryOptions(
options:
| ForeignDataWrapperOptions<RemoteServerType>
| Partial<ForeignDataWrapperOptions<RemoteServerType>>
| UserMappingOptions
| Partial<UserMappingOptions>,
isUpdate: boolean,
) {
const prefix = isUpdate ? 'SET ' : '';
return Object.entries(options)
.map(([key, value]) => `${prefix}${key} '${value}'`)
.join(', ');
}
}