Skip to content

Commit

Permalink
feat: ensure collection links are sorted with root as the first link (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
blacha authored Jul 11, 2023
1 parent 83e7420 commit 1fb1ad4
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
112 changes: 112 additions & 0 deletions src/commands/stac-github-import/__test__/stac.link.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { describe, it } from 'node:test';
import { sortLinks } from '../stac.github.import.js';
import assert from 'node:assert';

function shuffle<T>(array: T[]): T[] {
let currentIndex = array.length;
let randomIndex;

// While there remain elements to shuffle.
while (currentIndex !== 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;

// And swap it with the current element.
const randomItem = array[randomIndex] as T;
const currentItem = array[currentIndex] as T;
array[currentIndex] = randomItem;
array[randomIndex] = currentItem;
}

return array;
}

describe('sortLinks', () => {
it('should make root first', () => {
const links = [
{ rel: 'self', href: './collection.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4643.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4844.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_2932.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4750.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4807.json', type: 'application/json' },
{
rel: 'root',
href: 'https://linz-imagery.s3.ap-southeast-2.amazonaws.com/catalog.json',
type: 'application/json',
},
{ rel: 'item', href: './AY31_1000_4809.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4808.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_2934.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4744.json', type: 'application/json' },
];
sortLinks(links);
assert.equal(links[0]?.rel, 'root');
});

it('should sort alphabetically the items', () => {
const links = [
{ rel: 'self', href: './collection.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4643.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4844.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_2932.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4750.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4807.json', type: 'application/json' },
{
rel: 'root',
href: 'https://linz-imagery.s3.ap-southeast-2.amazonaws.com/catalog.json',
type: 'application/json',
},
{ rel: 'item', href: './AY31_1000_4809.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4808.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_2934.json', type: 'application/json' },
{ rel: 'item', href: './AY30_1000_4744.json', type: 'application/json' },
];
sortLinks(links);
const items = links.filter((f) => f.rel === 'item').map((f) => f.href);
assert.deepEqual(items, [
'./AY30_1000_4643.json',
'./AY30_1000_4744.json',
'./AY30_1000_4844.json',
'./AY31_1000_2932.json',
'./AY31_1000_2934.json',
'./AY31_1000_4750.json',
'./AY31_1000_4807.json',
'./AY31_1000_4808.json',
'./AY31_1000_4809.json',
]);
});

it('should be stable', () => {
const links = [
{ rel: 'self', href: './collection.json', type: 'application/json' },
{ rel: 'test', href: './AY30_1000_4643.json', type: 'application/json' },
{ rel: 'item', href: './AY30_100_4844.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_2932.json', type: 'application/json' },
{ rel: 'item', href: './AY31_10_4750.json', type: 'application/json' },
{ rel: 'item', href: './AY31_1000_4807.json', type: 'application/json' },
{
rel: 'root',
href: 'https://linz-imagery.s3.ap-southeast-2.amazonaws.com/catalog.json',
type: 'application/json',
},
{ rel: 'self', href: './AY31_500_4809.json', type: 'application/json' },
{ rel: 'item', href: './AY32_1000_4808.json', type: 'application/json' },
{ rel: 'root', href: 'https://AZ31_1000_2934.json', type: 'application/json' },
{ rel: 'fake', href: './AY30_1000_4744.json', type: 'application/json' },
];

sortLinks(links);
const sorted = links.map((f) => f.href);

for (let i = 0; i < 1000; i++) {
const shuffled = shuffle(links);
sortLinks(shuffled);
assert.deepEqual(
shuffled.map((f) => f.href),
sorted,
);
}
});
});
21 changes: 21 additions & 0 deletions src/commands/stac-github-import/stac.github.import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export const commandStacGithubImport = command({

logger.info({ href: selfLink.href }, 'Stac:SetRoot');

sortLinks(collection.links);

// Update the root link in the collection to the one defined in the repo template
const rootLink = collection.links.find((f) => f.rel === 'root');
if (rootLink) {
Expand Down Expand Up @@ -97,3 +99,22 @@ export const commandStacGithubImport = command({
execFileSync('git', ['push', 'origin', 'HEAD', '--force'], { cwd: gitRepo });
},
});

/** All other rel's are set between "root" and "item" */
const RelPriorityDefault = 50;
/** Ensure root rel are put before everything else */
const RelPriority: Record<string, number> = {
root: 0,
item: 100,
};

/** Sort collection links keeping non items at the top then items sorted by href */
export function sortLinks(links: st.StacLink[]): void {
links.sort((a, b) => {
if (a.rel === b.rel) return a.href.localeCompare(b.href);
const aRel = RelPriority[a.rel] ?? RelPriorityDefault;
const bRel = RelPriority[b.rel] ?? RelPriorityDefault;
if (aRel === bRel) return a.href.localeCompare(b.href);
return aRel - bRel;
});
}

0 comments on commit 1fb1ad4

Please sign in to comment.