Skip to content

Commit

Permalink
Add option to remove all documents from collections being seeding
Browse files Browse the repository at this point in the history
  • Loading branch information
EltonGarcia committed Mar 6, 2021
1 parent f7e0c4e commit a21c7cc
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 36 deletions.
2 changes: 2 additions & 0 deletions cli/README.md
Expand Up @@ -62,6 +62,7 @@ You can use the following parameters while using `seed` tool:
| `--db-options` | *`undefined`* | [MongoDB connection options](https://docs.mongodb.com/manual/reference/connection-string/) in a form of multiple `{key}={value}` values, separated by semicolon. For example: `ssl=true;maxPoolSize=50` |
| `--drop-database` | `false` | Drops entire database before data import |
| `--drop-collections` | `false` | Drops every collection which is being imported |
| `--remove-all-documents` | `false` | Delete all documents from every collection that is being imported |
| `--replace-id` | `false` | Replaces `id` property with `_id` for every document during data import |
| `--set-timestamps` | `false` | Sets `createdAt` and `updatedAt` timestamps for every document during data import |
| `--reconnect-timeout` | `10000` | Maximum time in milliseconds of waiting for successful MongoDB connection |
Expand All @@ -85,6 +86,7 @@ You can use the following environmental variables while using `seed` tool:
| `DB_OPTIONS` | *`undefined`* | [MongoDB connection options](https://docs.mongodb.com/manual/reference/connection-string/) in a form of multiple `{key}={value}` values, separated by semicolon. For example: `ssl=true;maxPoolSize=50`. |
| `DROP_DATABASE` | `false` | Drops entire database before data import |
| `DROP_COLLECTIONS` | `false` | Drops every collection which is being imported |
| `REMOVE_ALL_DOCUMENTS` | `false` | Delete all documents from every collection that is being imported |
| `REPLACE_ID` | `false` | Replaces `id` property with `_id` for every document during import; useful for ORMs |
| `SET_TIMESTAMPS` | `false` | Sets `createdAt` and `updatedAt` timestamps for every document during data import |
| `RECONNECT_TIMEOUT` | `10000` | Maximum time in milliseconds of waiting for successful MongoDB connection |
Expand Down
73 changes: 42 additions & 31 deletions cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 16 additions & 2 deletions cli/src/options.ts
Expand Up @@ -9,7 +9,7 @@ import { SeederDatabaseConfigObjectOptions } from 'mongo-seeding/dist/database';
import { Seeder, SeederCollectionReadingOptions } from 'mongo-seeding';

export const DEFAULT_INPUT_PATH = './';
export const DEFAULT_EXTENSIONS = ['ts', 'js', 'cjs', 'json']
export const DEFAULT_EXTENSIONS = ['ts', 'js', 'cjs', 'json'];

export const cliOptions: CommandLineOption[] = [
{
Expand Down Expand Up @@ -83,6 +83,12 @@ export const cliOptions: CommandLineOption[] = [
description: 'Drops every collection that is being imported',
type: Boolean,
},
{
name: 'remove-all-documents',
description:
'Delete all documents from every collection that is being imported',
type: Boolean,
},
{
name: 'replace-id',
description:
Expand Down Expand Up @@ -134,7 +140,13 @@ export const createConfigFromOptions = (
},
};

const mergedConfig = extend(true, config, defaultConfig, envConfig, commandLineConfig);
const mergedConfig = extend(
true,
config,
defaultConfig,
envConfig,
commandLineConfig,
);
mergedConfig.collectionReading = getCollectionReadingConfig(mergedConfig);
return mergedConfig;
};
Expand Down Expand Up @@ -183,6 +195,7 @@ function populateCommandLineOptions(
databaseReconnectTimeout: options['reconnect-timeout'],
dropDatabase: options['drop-database'],
dropCollections: options['drop-collections'],
removeAllDocuments: options['remove-all-documents'],
},
cli: {
dataPath: options['data'],
Expand Down Expand Up @@ -241,6 +254,7 @@ function populateEnvOptions(): PartialCliOptions {
: undefined,
dropDatabase: env.DROP_DATABASE === 'true',
dropCollections: env.DROP_COLLECTIONS === 'true',
removeAllDocuments: env.REMOVE_ALL_DOCUMENTS === 'true',
},
cli: {
ejsonParseCanonicalMode:
Expand Down
1 change: 1 addition & 0 deletions cli/src/types.ts
Expand Up @@ -15,6 +15,7 @@ export interface CommandLineArguments {
silent?: boolean;
'drop-database'?: boolean;
'drop-collections'?: boolean;
'remove-all-documents'?: boolean;
'replace-id'?: boolean;
'set-timestamps'?: boolean;
'db-protocol'?: string;
Expand Down
5 changes: 5 additions & 0 deletions cli/test/unit/options.ts
Expand Up @@ -31,6 +31,7 @@ describe('Options', () => {
'reconnect-timeout': 7000,
'drop-database': true,
'drop-collections': true,
'remove-all-documents': true,
'transpile-only': true,
'ejson-parse-canonical-mode': true,
},
Expand All @@ -40,6 +41,7 @@ describe('Options', () => {
databaseReconnectTimeout: 7000,
dropDatabase: true,
dropCollections: true,
removeAllDocuments: true,
},
cli: {
dataPath: './foo/bar',
Expand Down Expand Up @@ -182,6 +184,7 @@ describe('Options', () => {
RECONNECT_TIMEOUT: '7000',
DROP_DATABASE: 'true',
DROP_COLLECTIONS: 'true',
REMOVE_ALL_DOCUMENTS: 'true',
TRANSPILE_ONLY: 'true',
},
expected: {
Expand All @@ -190,6 +193,7 @@ describe('Options', () => {
databaseReconnectTimeout: 7000,
dropDatabase: true,
dropCollections: true,
removeAllDocuments: true,
},
cli: {
transpileOnly: true,
Expand Down Expand Up @@ -218,6 +222,7 @@ describe('Options', () => {
},
dropDatabase: false,
dropCollections: false,
removeAllDocuments: false,
},
cli: {
transpileOnly: false,
Expand Down
2 changes: 2 additions & 0 deletions core/src/config.ts
Expand Up @@ -12,6 +12,7 @@ export interface SeederConfig {
databaseReconnectTimeout: number; // maximum time of waiting for successful MongoDB connection in milliseconds. Ignored when `mongoClientOptions` are passed.
dropDatabase: boolean; // drops entire database before import
dropCollections: boolean; // drops collection before importing it
removeAllDocuments: boolean; // remove all documents from collections before importing it
mongoClientOptions?: MongoClientOptions; // optional MongoDB client options
collectionInsertManyOptions?: CollectionInsertManyOptions; // optional MongoDB collection import options
}
Expand All @@ -24,6 +25,7 @@ export const defaultSeederConfig: SeederConfig = {
databaseReconnectTimeout: 10000,
dropDatabase: false,
dropCollections: false,
removeAllDocuments: false,
};

/**
Expand Down
14 changes: 14 additions & 0 deletions core/src/database/database.ts
Expand Up @@ -83,6 +83,20 @@ export class Database {
return this.db.collection(collectionName).drop();
}

/**
* Remove all documents from a given collection
* if it exists.
*
* @param collectionName Collection name
*/
async removeAllDocumentsIfCollectionExists(collectionName: string) {
if (!(await this.ifCollectionExist(collectionName))) {
return;
}

return this.db.collection(collectionName).deleteMany({});
}

/**
* Closes connection with database.
*/
Expand Down
16 changes: 14 additions & 2 deletions core/src/index.ts
Expand Up @@ -62,7 +62,11 @@ export class Seeder {
let collections;
try {
const { CollectionPopulator } = require('./populator');
const populator = new CollectionPopulator(config.extensions, config.ejsonParseOptions, this.log);
const populator = new CollectionPopulator(
config.extensions,
config.ejsonParseOptions,
this.log,
);

this.log(`Reading collections from ${path}...`);
collections = populator.readFromPath(path);
Expand Down Expand Up @@ -104,7 +108,7 @@ export class Seeder {
this.log,
);

let database;
var database: Database | undefined;
try {
database = await databaseConnector.connect(config.database);

Expand All @@ -120,6 +124,14 @@ export class Seeder {
await database.drop();
}

if (config.removeAllDocuments) {
this.log('Removing all documents from collections...');
const promisses = collections.map((collection) =>
database!.removeAllDocumentsIfCollectionExists(collection.name),
);
await Promise.all(promisses);
}

await new CollectionImporter(
database,
config.collectionInsertManyOptions,
Expand Down
@@ -0,0 +1,4 @@
{
"number": 1,
"name": "one"
}
46 changes: 46 additions & 0 deletions core/test/integration/database.ts
Expand Up @@ -86,4 +86,50 @@ describe('Database', () => {
expectedCollections[1],
]);
});

it('should not drop any database or collection while removing all documents', async () => {
const expectedCollections = ['db-remove-doc-first', 'db-remove-doc-second'];

for (const collection of expectedCollections) {
await createCollection(database.db, collection);
}

await database.removeAllDocumentsIfCollectionExists(expectedCollections[0]);
await expect(listExistingCollections(database.db)).resolves.toStrictEqual(
expect.arrayContaining([expectedCollections[0], expectedCollections[1]]),
);
});

it('should remove all documents from collection without droping the db/collection', async () => {
const documents = [
{
value: 1,
},
{
name: 'two',
value: 2,
},
{
value: 3,
object: {
name: 'three',
},
},
];
const collection = 'db-remove-docs';

await database.insertDocumentsIntoCollection(documents, collection);
const inserted = await database.db.collection(collection).find().toArray();
expect(inserted).toHaveLength(documents.length);

await database.removeAllDocumentsIfCollectionExists(collection);
const result = await database.db.collection(collection).find().toArray();
expect(result).toHaveLength(0);
});

it('should not crash if collection does no exists', async () => {
await database.removeAllDocumentsIfCollectionExists(
'non-existing-collection',
);
});
});

0 comments on commit a21c7cc

Please sign in to comment.