Skip to content

Commit

Permalink
Sort exports
Browse files Browse the repository at this point in the history
Fix merge option
Add utils tests
  • Loading branch information
u12206050 committed Nov 15, 2023
1 parent 8d0a2b1 commit 27bfe5c
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
node_modules
dist
dist-test
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Version 1.4.2
# Version 1.5.0 ⚠️

- **Sorts exported object keys**
- Fixes issue where the order of the exported object keys would change between exports, causing unnecessary changes in git.
- **merge option fixed**
- The merge option was introduced in version 1.3.0, but it was not working as intended. This has now been fixed.

## Version 1.4.2

- Add `import-schema` and `export-schema` commands to import/export only the schema.

Expand Down
30 changes: 28 additions & 2 deletions package-lock.json

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

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "directus-extension-schema-sync",
"description": "Sync schema and data betwreen Directus instances",
"icon": "sync_alt",
"version": "1.4.2",
"version": "1.5.0",
"repository": {
"type": "git",
"url": "https://github.com/bcc-code/directus-schema-sync.git"
Expand All @@ -29,11 +29,14 @@
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w --no-minify",
"link": "directus-extension link"
"link": "directus-extension link",
"pre-test": "tsc -p tsconfig.test.json",
"test": "npm run pre-test && node --test dist-test/"
},
"devDependencies": {
"@directus/extensions-sdk": "10.1.11",
"@directus/types": "^10.1.6",
"@types/keyv": "^4.2.0",
"@types/node": "^20.8.7",
"typescript": "^5.2.2"
}
Expand Down
13 changes: 7 additions & 6 deletions src/collectionExporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ApiExtensionContext, Item, PrimaryKey, Query } from '@directus/types';
import { readFile, writeFile } from 'fs/promises';
import { condenseAction } from './condenseAction.js';
import type { CollectionExporterOptions, IExporter, IGetItemsService, ItemsService, JSONString } from './types';
import { ExportHelper, getDiff } from './utils.js';
import { ExportHelper, getDiff, sortObject } from './utils.js';

const DEFAULT_COLLECTION_EXPORTER_OPTIONS: CollectionExporterOptions = {
excludeFields: [],
Expand Down Expand Up @@ -109,7 +109,7 @@ class CollectionExporter implements IExporter {
const itemsSvc = await this._getService();
const { query } = await this.settings();

const items = await itemsSvc.readByQuery(query);
let items = await itemsSvc.readByQuery(query);
if (!items.length) return '';

if (this.options.onExport) {
Expand All @@ -118,10 +118,11 @@ class CollectionExporter implements IExporter {
const alteredItem = await this.options.onExport(item, itemsSvc);
if (alteredItem) alteredItems.push(alteredItem);
}
return JSON.stringify(alteredItems, null, 2);
} else {
return JSON.stringify(items, null, 2);
}

items = alteredItems;
}

return JSON.stringify(sortObject(items), null, 2);
}

public async loadJSON(json: JSONString | null, merge = false) {
Expand Down
2 changes: 1 addition & 1 deletion src/exportManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class ExportManager {

// SECOND: Import if needed
public async loadAll(merge = false) {
await this._loadNextExporter(0);
await this._loadNextExporter(0, merge);
}

protected async _loadNextExporter(i = 0, merge = false) {
Expand Down
104 changes: 104 additions & 0 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import assert from "node:assert";
import { describe, it } from "node:test";
import { deepEqual, getDiff, sortObject } from "./utils.js";

describe('sortObject', () => {
it('should sort object keys alphabetically', () => {
const input = { c: 1, a: 2, b: 3 };
const assertedOutput = { a: 2, b: 3, c: 1 };
assert.deepStrictEqual(sortObject(input), assertedOutput);
});

it('should sort nested object keys alphabetically', () => {
const input = { c: 1, a: { d: 4, b: 3 }, e: 5 };
const assertedOutput = { a: { b: 3, d: 4 }, c: 1, e: 5 };
assert.deepStrictEqual(sortObject(input), assertedOutput);
});

it('should sort array elements recursively', () => {
const input = [{ c: 1, a: 2 }, { b: 3 }];
const assertedOutput = [{ a: 2, c: 1 }, { b: 3 }];
assert.deepStrictEqual(sortObject(input), assertedOutput);
});

it('should return input if it is not an object', () => {
assert.deepStrictEqual(sortObject(null as any), null);
assert.deepStrictEqual(sortObject(42 as any), 42);
assert.deepStrictEqual(sortObject('hello' as any), 'hello');
});
});

describe('getDiff', () => {
it('should return the entire new object if the old object is null', () => {
const newObj = { a: 1, b: 2 };
const oldObj = null;
const assertedOutput = { a: 1, b: 2 };
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
});

it('should return null if the new and old objects are equal', () => {
const newObj = { a: 1, b: 2 };
const oldObj = { a: 1, b: 2 };
assert.deepStrictEqual(getDiff(newObj, oldObj), null);
});

it('should return only the different properties between the new and old objects', () => {
const newObj = { a: 1, b: 2, c: 3 };
const oldObj = { a: 1, b: 3, d: 4 };
const assertedOutput = { b: 2, c: 3 };
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
});

it('should handle nested objects', () => {
const newObj = { a: 1, b: { c: 2, d: 3 } };
const oldObj = { a: 1, b: { c: 2, d: 4 } };
const assertedOutput = { b: { d: 3 } };
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
});

it('should handle arrays', () => {
const newObj = { a: 1, b: [1, 2, 3] };
const oldObj = { a: 1, b: [1, 2, 4] };
const assertedOutput = { b: [1, 2, 3] };
assert.deepStrictEqual(getDiff(newObj, oldObj), assertedOutput);
});
});

describe('deepEqual', () => {
it('should return true for equal objects', () => {
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
assert.strictEqual(deepEqual(obj1, obj2), true);
});

it('should return false for different objects', () => {
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 3 } };
assert.strictEqual(deepEqual(obj1, obj2), false);
});

it('should return true for equal arrays', () => {
const arr1 = [1, 2, { a: 3 }];
const arr2 = [1, 2, { a: 3 }];
assert.strictEqual(deepEqual(arr1, arr2), true);
});

it('should return false for different arrays', () => {
const arr1 = [1, 2, { a: 3 }];
const arr2 = [1, 2, { a: 4 }];
assert.strictEqual(deepEqual(arr1, arr2), false);
});

it('should return true for equal primitives', () => {
assert.strictEqual(deepEqual(1, 1), true);
assert.strictEqual(deepEqual('hello', 'hello'), true);
assert.strictEqual(deepEqual(null, null), true);
assert.strictEqual(deepEqual(undefined, undefined), true);
});

it('should return false for different primitives', () => {
assert.strictEqual(deepEqual(1, 2), false);
assert.strictEqual(deepEqual('hello', 'world'), false);
assert.strictEqual(deepEqual(null, undefined), false);
});
});
18 changes: 18 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,21 @@ export function getDiff(newObj: Record<any, any>, oldObj: any) {
});
return isDifferent ? result : null;
}

export function sortObject<T extends Record<string, any>>(obj: T): T;
export function sortObject<T>(obj: T[]): T[];
export function sortObject<T extends Record<string, any> | T[]>(obj: T): T {
if (typeof obj !== 'object' || obj === null) {
return obj;
}

if (Array.isArray(obj)) {
return obj.map(sortObject) as unknown as T;
}

const sortedObj: Record<string, any> = {};
Object.keys(obj).sort().forEach(key => {
sortedObj[key] = sortObject((obj as Record<string, any>)[key]);
});
return sortedObj as T;
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@
"rootDir": "./src"
},
"include": ["./src/**/*.ts"]
}
}
30 changes: 30 additions & 0 deletions tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM"],
"module": "ES2022",
"moduleResolution": "node",
"strict": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUncheckedIndexedAccess": true,
"noUnusedParameters": true,
"alwaysStrict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"resolveJsonModule": false,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"allowSyntheticDefaultImports": true,
"isolatedModules": true,
"rootDir": "./src",
"outDir": "./dist-test",
},
"include": ["src/utils.ts", "src/utils.test.ts"]
}

0 comments on commit 27bfe5c

Please sign in to comment.