[feat][Remote objects] Edit a connection (for pg) (#5210)

## Context
#4774 

## How was it tested
Locally

## In further PRs
- Update connection status upon page change
- Adapt Info banner to dark mode
- placeholders for form
This commit is contained in:
Marie
2024-04-30 17:46:30 +02:00
committed by GitHub
parent 3bf9045990
commit 1b2ed80c1c
39 changed files with 727 additions and 195 deletions

View File

@ -5,8 +5,8 @@ import { isDefined } from 'class-validator';
import {
ForeignDataWrapperOptions,
RemoteServerType,
UserMappingOptions,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
@Injectable()
export class ForeignDataWrapperQueryFactory {
@ -39,7 +39,7 @@ export class ForeignDataWrapperQueryFactory {
createUserMapping(
foreignDataWrapperId: string,
userMappingOptions: UserMappingOptions,
userMappingOptions: UserMappingOptionsInput,
) {
// 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}')`;
@ -47,7 +47,7 @@ export class ForeignDataWrapperQueryFactory {
updateUserMapping(
foreignDataWrapperId: string,
userMappingOptions: Partial<UserMappingOptions>,
userMappingOptions: Partial<UserMappingOptionsInput>,
) {
const options = this.buildUpdateUserMappingOptions(userMappingOptions);
@ -82,7 +82,7 @@ export class ForeignDataWrapperQueryFactory {
}
private buildUpdateUserMappingOptions(
userMappingOptions?: Partial<UserMappingOptions>,
userMappingOptions?: Partial<UserMappingOptionsInput>,
) {
const setStatements: string[] = [];

View File

@ -6,9 +6,8 @@ import GraphQLJSON from 'graphql-type-json';
import {
ForeignDataWrapperOptions,
RemoteServerType,
UserMappingOptions,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options-input.utils';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
@InputType()
export class CreateRemoteServerInput<T extends RemoteServerType> {
@ -20,7 +19,7 @@ export class CreateRemoteServerInput<T extends RemoteServerType> {
@IsOptional()
@Field(() => UserMappingOptionsInput, { nullable: true })
userMappingOptions?: UserMappingOptions;
userMappingOptions?: UserMappingOptionsInput;
@IsOptional()
@Field(() => String, { nullable: true })

View File

@ -7,6 +7,7 @@ import {
ForeignDataWrapperOptions,
RemoteServerType,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { GetUserMappingOptions } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
@ObjectType('RemoteServer')
export class RemoteServerDTO<T extends RemoteServerType> {
@ -23,6 +24,14 @@ export class RemoteServerDTO<T extends RemoteServerType> {
@Field(() => GraphQLJSON, { nullable: true })
foreignDataWrapperOptions?: ForeignDataWrapperOptions<T>;
@IsOptional()
@Field(() => GetUserMappingOptions, { nullable: true })
userMappingOptions?: GetUserMappingOptions;
@IsOptional()
@Field(() => String, { nullable: true })
schema?: string;
@HideField()
workspaceId: string;

View File

@ -6,9 +6,8 @@ import GraphQLJSON from 'graphql-type-json';
import {
ForeignDataWrapperOptions,
RemoteServerType,
UserMappingOptions,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options-input.utils';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
@InputType()
export class UpdateRemoteServerInput<T extends RemoteServerType> {
@ -21,5 +20,9 @@ export class UpdateRemoteServerInput<T extends RemoteServerType> {
@IsOptional()
@Field(() => UserMappingOptionsInput, { nullable: true })
userMappingOptions?: Partial<UserMappingOptions>;
userMappingOptions?: Partial<UserMappingOptionsInput>;
@IsOptional()
@Field(() => String, { nullable: true })
schema?: string;
}

View File

@ -10,6 +10,7 @@ import {
} from 'typeorm';
import { RemoteTableEntity } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.entity';
import { UserMappingOptionsInput as UserMappingOptions } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
export enum RemoteServerType {
@ -26,12 +27,6 @@ export type ForeignDataWrapperOptions<T extends RemoteServerType> =
T extends RemoteServerType.POSTGRES_FDW
? PostgresForeignDataWrapperOptions
: never;
export type UserMappingOptions = {
username: string;
password: string;
};
@Entity('remoteServer')
export class RemoteServerEntity<T extends RemoteServerType> {
@PrimaryGeneratedColumn('uuid')

View File

@ -5,6 +5,7 @@ import {
} from '@nestjs/common';
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import isEmpty from 'lodash.isempty';
import { v4 } from 'uuid';
import { DataSource, EntityManager, Repository } from 'typeorm';
@ -162,7 +163,9 @@ export class RemoteServerService<T extends RemoteServerType> {
partialRemoteServerWithUpdates,
);
if (partialRemoteServerWithUpdates.foreignDataWrapperOptions) {
if (
!isEmpty(partialRemoteServerWithUpdates.foreignDataWrapperOptions)
) {
const foreignDataWrapperQuery =
this.foreignDataWrapperQueryFactory.updateForeignDataWrapper({
foreignDataWrapperId,
@ -173,7 +176,7 @@ export class RemoteServerService<T extends RemoteServerType> {
await entityManager.query(foreignDataWrapperQuery);
}
if (partialRemoteServerWithUpdates.userMappingOptions) {
if (!isEmpty(partialRemoteServerWithUpdates.userMappingOptions)) {
const userMappingQuery =
this.foreignDataWrapperQueryFactory.updateUserMapping(
foreignDataWrapperId,

View File

@ -1,10 +1,12 @@
import { BadRequestException } from '@nestjs/common';
import { isDefined } from 'class-validator';
import {
RemoteServerEntity,
RemoteServerType,
UserMappingOptions,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { UserMappingOptionsInput } from 'src/engine/metadata-modules/remote-server/utils/user-mapping-options.utils';
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
@ -13,7 +15,7 @@ export type DeepPartial<T> = {
const buildUserMappingOptionsQuery = (
parameters: any[],
parametersPositions: object,
userMappingOptions: DeepPartial<UserMappingOptions>,
userMappingOptions: DeepPartial<UserMappingOptionsInput>,
): string | null => {
const shouldUpdateUserMappingOptionsPassword = isDefined(
userMappingOptions?.password,
@ -130,6 +132,10 @@ export const updateRemoteServerRawQuery = (
options.push(fwdOptionsQuery);
}
if (options.length < 1) {
throw new BadRequestException('No fields to update');
}
const rawQuery = `UPDATE metadata."remoteServer" SET ${options.join(
', ',
)} WHERE "id"= $1 RETURNING *`;

View File

@ -1,4 +1,4 @@
import { InputType, Field } from '@nestjs/graphql';
import { InputType, Field, ObjectType } from '@nestjs/graphql';
import { IsOptional } from 'class-validator';
@ -12,3 +12,10 @@ export class UserMappingOptionsInput {
@Field(() => String, { nullable: true })
password: string;
}
@ObjectType()
export class GetUserMappingOptions {
@IsOptional()
@Field(() => String, { nullable: true })
username: string;
}