Skip to content

Commit

Permalink
Merge smarter jupyter values
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewballantyne committed May 10, 2024
1 parent baa33fc commit 3e391d0
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 2 deletions.
69 changes: 69 additions & 0 deletions backend/src/__tests__/objUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { smartMergeArraysWithNameObjects } from '../utils/objUtils';

describe('objUtils', () => {
describe('smartMergeArraysWithNameObjects', () => {
/** we only use the first two params of the customization utility, so ignore the last 3 */
const smartMergeArraysWithNameObjectsWithUsedParams = (v1: any, v2: any) =>
smartMergeArraysWithNameObjects(v1, v2, undefined, undefined, undefined);

it('should do nothing with nulls', () => {
expect(smartMergeArraysWithNameObjectsWithUsedParams(null, null)).toBe(undefined);
});

it('should do nothing with objects', () => {
expect(
smartMergeArraysWithNameObjectsWithUsedParams({ a: true }, { a: false, b: true }),
).toBe(undefined);
});

it('should do nothing with string[]', () => {
expect(smartMergeArraysWithNameObjectsWithUsedParams(['test'], ['test2'])).toBe(undefined);
});

it('should do nothing with invalid object arrays', () => {
expect(
smartMergeArraysWithNameObjectsWithUsedParams([{ id: 'test' }], [{ id: 'test2' }]),
).toBe(undefined);
});

it('should replace 2nd object if given two same correct objects arrays', () => {
expect(
smartMergeArraysWithNameObjectsWithUsedParams(
[{ name: 'test', value: '1' }],
[{ name: 'test', value: '2' }],
),
).toEqual([{ name: 'test', value: '2' }]);
});

it('should add 2nd object if given two different correct object arrays', () => {
expect(
smartMergeArraysWithNameObjectsWithUsedParams(
[{ name: 'test', value: '1' }],
[{ name: 'test2', value: '2' }],
),
).toEqual([
{ name: 'test', value: '1' },
{ name: 'test2', value: '2' },
]);
});

it('should replace and add as appropriate if given two correct object arrays', () => {
expect(
smartMergeArraysWithNameObjectsWithUsedParams(
[
{ name: 'test', value: '1' },
{ name: 'test3', value: '3' },
],
[
{ name: 'test', value: '1b' },
{ name: 'test2', value: '2' },
],
),
).toEqual([
{ name: 'test', value: '1b' },
{ name: 'test3', value: '3' },
{ name: 'test2', value: '2' },
]);
});
});
});
10 changes: 8 additions & 2 deletions backend/src/utils/notebookUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getDashboardConfig } from './resourceUtils';
import { merge } from 'lodash';
import { mergeWith } from 'lodash';
import {
ContainerResourceAttributes,
EnvironmentVariable,
Expand Down Expand Up @@ -31,6 +31,7 @@ import {
import { DEFAULT_NOTEBOOK_SIZES, DEFAULT_PVC_SIZE, MOUNT_PATH } from './constants';
import { FastifyRequest } from 'fastify';
import { verifyEnvVars } from './envUtils';
import { smartMergeArraysWithNameObjects } from './objUtils';
import { getImageInfo } from '../routes/api/images/imageUtils';

export const generateNotebookNameFromUsername = (username: string): string =>
Expand Down Expand Up @@ -544,7 +545,12 @@ export const updateNotebook = async (
},
};

const notebookAssembled = merge({}, importantOldNotebookDetails, serverNotebook);
const notebookAssembled = mergeWith(
{},
importantOldNotebookDetails,
serverNotebook,
smartMergeArraysWithNameObjects,
);

const response = await fastify.kube.customObjectsApi.patchNamespacedCustomObject(
'kubeflow.org',
Expand Down
30 changes: 30 additions & 0 deletions backend/src/utils/objUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { MergeWithCustomizer } from 'lodash';

/**
* When given two object arrays that have "name" keys, replace when keys are the same, or add to
* the end when new key.
*
* Note: returns `undefined` if invalid data is provided.
*
* @see mergeWith -- lodash mergeWith customizer
*/
export const smartMergeArraysWithNameObjects: MergeWithCustomizer = (objValue, srcValue) => {
type GoodArray = { name: string }[];
const isGoodArray = (v: any): v is GoodArray => Array.isArray(v) && v.length > 0 && v[0].name;
if (isGoodArray(objValue) && isGoodArray(srcValue)) {
// Arrays with objects that have a .name property, sync on merge for the name
return srcValue.reduce<GoodArray>((newArray, elm) => {
const key = elm.name;
const index = newArray.findIndex(({ name }) => name === key);
if (index >= 0) {
// existing value, replace
newArray[index] = elm;
} else {
// didn't find existing name, add to end
newArray.push(elm);
}

return newArray;
}, objValue);
}
};

0 comments on commit 3e391d0

Please sign in to comment.