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

feat(ui): list connections refactor, show end user profile #2897

Merged
merged 15 commits into from
Oct 30, 2024

Conversation

bodinsamuel
Copy link
Collaborator

@bodinsamuel bodinsamuel commented Oct 25, 2024

Describe your changes

Fixes https://linear.app/nango/issue/NAN-1812/ui-for-userorganization-info-connections-screen

Major

  • Refactor the connections list page
    Show end user profile, add avatar, search (connection id, profile display name), filter by integration, filter by errors

  • Move endpoint to file GET /api/v1/connections

  • Move endpoint to file GET /connections

Minor

  • Add new endpoint /connections/count
    It was used to display the error mark, since the main endpoints has pagination it was no longer good, it's also a bit more efficient now.

  • Add Connection button now uses Connect UI by default
    We still have the fallback with the old version for a few weeks

  • Allow Connect UI for all env
    Note the fix for self hosting is in a next PR

Screen.Recording.2024-10-29.at.16.57.07.mov

New query seems acceptable (results in prod replica):

EXPLAIN SELECT
	*
FROM (
	SELECT
		row_to_json(_nango_connections.*) AS connection,
		row_to_json(end_users.*) AS end_user,
		(
			SELECT
				COALESCE(json_agg(json_build_object('type', TYPE, 'log_id', log_id)), '[]'::json)
			FROM
				_nango_active_logs
			WHERE
				_nango_connections.id = _nango_active_logs.connection_id
				AND _nango_active_logs.active = TRUE) AS active_logs, "_nango_configs"."provider"
		FROM
			"_nango_connections"
			INNER JOIN "_nango_configs" ON "_nango_connections"."config_id" = "_nango_configs"."id"
			LEFT JOIN "end_users" ON "end_users"."id" = "_nango_connections"."end_user_id"
		WHERE
			"_nango_connections"."environment_id" = 18
			AND "_nango_connections"."deleted" = FALSE
			and(connection_id ILIKE '%samuel%'
				OR end_users.display_name ILIKE '%samuel%'
				OR end_users.email ILIKE '%samuel%')
		ORDER BY
			"_nango_connections"."created_at" DESC
		LIMIT 20) AS "rows"
Subquery Scan on rows  (cost=44.07..46.75 rows=1 width=104)
  ->  Limit  (cost=44.07..46.74 rows=1 width=112)
        ->  Result  (cost=44.07..46.74 rows=1 width=112)
              ->  Sort  (cost=44.07..44.08 rows=1 width=84)
                    Sort Key: _nango_connections.created_at DESC
                    ->  Nested Loop  (cost=11.99..44.06 rows=1 width=84)
                          ->  Hash Left Join  (cost=11.71..41.80 rows=1 width=1997)
                                Hash Cond: (_nango_connections.end_user_id = end_users.id)
"                                Filter: (((_nango_connections.connection_id)::text ~~* '%samuel%'::text) OR ((end_users.display_name)::text ~~* '%samuel%'::text) OR ((end_users.email)::text ~~* '%samuel%'::text))"
                                ->  Bitmap Heap Scan on _nango_connections  (cost=2.83..32.85 rows=27 width=899)
                                      Recheck Cond: ((environment_id = 18) AND (NOT deleted))
                                      ->  Bitmap Index Scan on idx_connections_envid_connectionid_provider_where_deleted  (cost=0.00..2.82 rows=27 width=0)
                                            Index Cond: (environment_id = 18)
                                ->  Hash  (cost=6.17..6.17 rows=217 width=1177)
                                      ->  Seq Scan on end_users  (cost=0.00..6.17 rows=217 width=1177)
                          ->  Index Scan using _nango_configs_pkey on _nango_configs  (cost=0.29..2.26 rows=1 width=12)
                                Index Cond: (id = _nango_connections.config_id)
              SubPlan 1
                ->  Aggregate  (cost=2.63..2.64 rows=1 width=32)
                      ->  Index Scan using idx_activelogs_connectionid_where_active on _nango_active_logs  (cost=0.41..2.63 rows=1 width=25)
                            Index Cond: (connection_id = _nango_connections.id)

@bodinsamuel bodinsamuel self-assigned this Oct 25, 2024
Copy link

linear bot commented Oct 25, 2024

async getAllNames(environment_id: number): Promise<string[]> {
const configs = await this.listProviderConfigs(environment_id);
return configs.map((config) => config.unique_key);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Dead code

@@ -50,7 +50,7 @@ export const MultiSelect: React.FC<MultiSelectArgs<any>> = ({ label, options, se
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button variant="zombieGray" size={'xs'} className={cn('text-text-light-gray', isDirty && 'text-white')}>
<Button variant="zombieGray" size={'sm'} className={cn('text-text-light-gray', isDirty && 'text-white')}>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Size were incorrect

subQuery.where(function () {
this.whereRaw('connection_id ILIKE ?', `%${search}%`)
.orWhereRaw('end_users.display_name ILIKE ?', `%${search}%`)
.orWhereRaw('end_users.email ILIKE ?', `%${search}%`);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This part is not going to scale forever but looks okay for now

public async getAllNames(environment_id: number): Promise<string[]> {
const connections = await this.listConnections(environment_id);
return [...new Set(connections.map((config) => config.connection_id))];
return await query;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Check PR desc for query plan

@bodinsamuel bodinsamuel marked this pull request as ready for review October 29, 2024 16:18
packages/shared/lib/services/config.service.ts Outdated Show resolved Hide resolved
return { total: 0, withError: 0 };
}

return { total: Number(res.total_connection), withError: Number(res.with_error) };
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this function return a Result instead of 0s when there is an error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

imo this count is not a critical feature, if it fails I don't want to cut the rest of the API call. wdyt?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The result can be handled so Err is not blocking but at least this function semantic will be stronger. For example differentiating an error to having zero conns

.from(`_nango_connections`)
.select<{ total_connection: string; with_error: string }>(
db.knex.raw('COUNT(_nango_connections.*) as total_connection'),
db.knex.raw('COUNT(_nango_connections.*) FILTER (WHERE _nango_active_logs.type IS NOT NULL) as with_error')
Copy link
Collaborator

Choose a reason for hiding this comment

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

TIL I learn about pg FILTER :)

packages/shared/lib/services/connection.service.ts Outdated Show resolved Hide resolved
packages/webapp/src/utils/avatar.ts Outdated Show resolved Hide resolved
packages/webapp/src/utils/avatar.ts Show resolved Hide resolved
@bodinsamuel bodinsamuel requested a review from TBonnin October 30, 2024 13:00
@bodinsamuel bodinsamuel merged commit 7f4a42c into master Oct 30, 2024
21 checks passed
@bodinsamuel bodinsamuel deleted the sam/feat/ui-list-connections branch October 30, 2024 13:40
bodinsamuel added a commit that referenced this pull request Oct 30, 2024
## Describe your changes

Closes #2920 

- Reup search in `GET /connections`
This feature was forgotten will moving the endpoint code to the new
format in #2897
Note that the behavior is slightly different now, not sure if I should
introduce a new `search` prop to filter on everything and keep
`connectionId` as it was before (filtering only connectionId)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants