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

verify remote collection before local collection deletion #2701

Merged
merged 1 commit into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class QueryCollectionService {
}

async createRemoteCollection(
collection: CreateDTO<IQueryCollection>,
collection: CreateDTO<IQueryCollection> | IQueryCollection,
workspaceId?: WorkspaceId,
teamId?: TeamId
) {
Expand All @@ -74,9 +74,9 @@ export class QueryCollectionService {
return;
}

// TODO: Handle parent collection ID - at the moment, we don't know the parent collection server ID
const res = await this.api.createQueryCollection(
collection,
// root collection has no parent collection ID
undefined,
workspaceId,
teamId
Expand All @@ -86,6 +86,22 @@ export class QueryCollectionService {
throw new Error('could not create the collection');
}

if ('id' in collection) {
// Create subcollections
const subcollections = await this.getSubcollections(collection.id);
for (let i = 0; i < subcollections.length; i++) {
const subcollection = subcollections[i];
if (subcollection?.id) {
await this.api.createQueryCollection(
subcollection,
res.id,
workspaceId,
teamId
);
}
}
}

return res.id;
}

Expand Down Expand Up @@ -122,7 +138,22 @@ export class QueryCollectionService {
// Create remote collection
// Delete local collection
const localCollection = await this.mustGetLocalCollection(collectionId);
await this.createRemoteCollection(localCollection);
const resId = await this.createRemoteCollection(localCollection);
Copy link

Choose a reason for hiding this comment

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

issue (complexity): Consider extracting verification and subcollection creation logic into dedicated methods to improve code organization

The code could be simplified while maintaining the same functionality:

  1. Extract verification logic into a dedicated method:
private async verifyRemoteCollection(remoteId: string, localCollection: IQueryCollection) {
  const remote = await this.api.getCollection(remoteId);
  if (!remote) {
    throw new Error('Failed to retrieve the remote collection');
  }
  if (remote.queries.length !== localCollection.queries.length) {
    throw new Error('Query count mismatch');
  }
  return remote;
}
  1. Extract subcollection creation into a focused method:
private async createSubcollections(
  parentId: string, 
  originalCollectionId: string,
  workspaceId?: WorkspaceId,
  teamId?: TeamId
) {
  const subcollections = await this.getSubcollections(originalCollectionId);
  await Promise.all(subcollections.map(sub => 
    sub?.id && this.api.createQueryCollection(sub, parentId, workspaceId, teamId)
  ));
}

This simplifies the main methods:

async createRemoteCollection(
  collection: CreateDTO<IQueryCollection> | IQueryCollection,
  workspaceId?: WorkspaceId,
  teamId?: TeamId
) {
  if (!(await this.canApplyRemote())) return;

  const res = await this.api.createQueryCollection(collection, undefined, workspaceId, teamId);
  if (!res) throw new Error('could not create the collection');

  if ('id' in collection) {
    await this.createSubcollections(res.id, collection.id, workspaceId, teamId);
  }
  return res.id;
}

async transformCollectionToRemoteCollection(collectionId: CollectionID) {
  if (!(await this.canApplyRemote())) return;

  const localCollection = await this.mustGetLocalCollection(collectionId);
  const remoteId = await this.createRemoteCollection(localCollection);
  await this.verifyRemoteCollection(remoteId, localCollection);
  await this.deleteLocalCollection(collectionId);
}


// Verify remote collection is created properly before deleting it
if (!resId) {
throw new Error('Remote collection creation failed');
}

// Verify all subqueries and their content
const remoteCollection = await this.api.getCollection(resId);
if (!remoteCollection) {
throw new Error('Failed to retrieve the remote collection');
}

if (remoteCollection.queries.length !== localCollection.queries.length) {
throw new Error('Query count mismatch');
}
await this.deleteLocalCollection(collectionId);
}

Expand Down Expand Up @@ -160,11 +191,7 @@ export class QueryCollectionService {
return id;
}

async updateQuery(
collectionId: CollectionID,
queryId: QueryID,
query: IQuery
) {
async updateQuery(collectionId: CollectionID, queryId: QueryID, query: IQuery) {
const collection = await this.getCollectionByID(collectionId);

if (!collection) {
Expand Down Expand Up @@ -202,9 +229,7 @@ export class QueryCollectionService {
}

async getLocalCollectionByID(collectionId: CollectionID) {
const localCollection = await this.storage.queryCollections.get(
collectionId
);
const localCollection = await this.storage.queryCollections.get(collectionId);
if (!localCollection) {
collectionId = this.getAlternateCollectionID(collectionId);
}
Expand Down Expand Up @@ -371,10 +396,7 @@ export class QueryCollectionService {
return;
}

return this.api.updateCollection(
collection.id.toString(),
modifiedCollection
);
return this.api.updateCollection(collection.id.toString(), modifiedCollection);
}
return this.updateLocalCollection(collectionId, modifiedCollection);
}
Expand All @@ -390,9 +412,7 @@ export class QueryCollectionService {
}

async getExportCollectionData(collectionId: CollectionID) {
const collectionTree = await this.getCollectionTreeByCollectionId(
collectionId
);
const collectionTree = await this.getCollectionTreeByCollectionId(collectionId);

if (!collectionTree) {
throw new Error('Collection not found!');
Expand All @@ -401,9 +421,7 @@ export class QueryCollectionService {
return this.getExportCollectionDataFromCollectionTree(collectionTree);
}

getExportCollectionDataFromCollectionTree(
collectionTree: IQueryCollectionTree
) {
getExportCollectionDataFromCollectionTree(collectionTree: IQueryCollectionTree) {
const exportCollectionData: ExportCollectionState = {
version: 1,
type: 'collection',
Expand Down Expand Up @@ -502,10 +520,7 @@ export class QueryCollectionService {
observableFrom(this.getSubcollections(collectionId, recursive));
}

moveCollection(
collectionId: CollectionID,
newParentCollectionId: CollectionID
) {
moveCollection(collectionId: CollectionID, newParentCollectionId: CollectionID) {
return this.moveLocalCollection(collectionId, newParentCollectionId);
// TODO: move collection remote
}
Expand All @@ -529,14 +544,12 @@ export class QueryCollectionService {
}

// '/coll-z', id: 456
const newParentCollection = await this.getLocalCollectionByID(
newParentCollectionId
);
const newParentCollection =
await this.getLocalCollectionByID(newParentCollectionId);
if (!newParentCollection) {
throw new Error('Could not retrieve parent collection');
}
const newParentCollectionParentPath =
newParentCollection.parentPath ?? '';
const newParentCollectionParentPath = newParentCollection.parentPath ?? '';
const newParentSubcollectionParentPath = `${newParentCollectionParentPath}${COLLECTION_PATH_SEPARATOR}${newParentCollectionId}`;

// '/coll-a'
Expand Down Expand Up @@ -700,9 +713,7 @@ export class QueryCollectionService {
* @param parentCollectionId
*/
private async getSubcollectionParentPath(parentCollectionId: CollectionID) {
const parentCollection = await this.getLocalCollectionByID(
parentCollectionId
);
const parentCollection = await this.getLocalCollectionByID(parentCollectionId);
const parentCollectionParentPath = parentCollection?.parentPath ?? '';

return `${parentCollectionParentPath}${COLLECTION_PATH_SEPARATOR}${parentCollectionId}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EMPTY, firstValueFrom, from } from 'rxjs';
import { EMPTY, firstValueFrom, from, of } from 'rxjs';

import { tap, map, switchMap, take } from 'rxjs/operators';
import { tap, map, switchMap, take, catchError } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { select, Store } from '@ngrx/store';

Expand Down Expand Up @@ -512,6 +512,11 @@ export class WindowService {
return from(
this.collectionService.getCollectionByID(data.layout.collectionId)
).pipe(
catchError(() => {
// continue to evaluation logic in map() below if collection is not found
// (EMPTY would stop the observable chain)
return of(undefined);
}),
map((collection) => {
if (collection) {
const query = collection.queries.find(
Expand Down
Loading