Skip to content

Commit

Permalink
feat: added WebsiteFileLink
Browse files Browse the repository at this point in the history
  • Loading branch information
dereekb committed Aug 15, 2022
1 parent bbc3af3 commit dc58b5c
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 19 deletions.
11 changes: 8 additions & 3 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ It should look like this:
### Emulators In Docker
We use Docker to run the emulators within a Docker Container. This lets us not worry about the host system having Java and other dependencies installed.

The emulators require having `service_account.json` available. Make sure you get a valid service account JSON key file and add it to the workspace.
The emulator do not require having `service_account.json` available, but would use it if you choose not to enable certain emulators. Make sure you get a valid service account JSON key file and add it to the workspace if you want to do this.

To run the emulators execute:

Expand All @@ -174,10 +174,11 @@ Behind the scenes, `nx serve demo-api` runs two commands in parallel:

Any changes made to the `demo-api` package will trigger. VS Code to build the project and update our dist, causing the functions emulator to update. This lets us develop in real time with an active emulated database.

You can read more about how this code base has enabled hot reloading in the Hot Reloading section below.

### Testing
Some tests run in just the node context, while others are run with the firebase emulators.


### CI Testing Output
A couple things are configured for the CI to enable reports to be output to `.reports/jest`:

Expand All @@ -193,7 +194,11 @@ These three items come together and enable jest-junit to do it's job, and circle
By default, Firebase API calls have their body parsed by express. This occurs before it reaches our demo-api's onRequest express server. If you add in additional body parsers and handlers be sure to update the request appropriately.

### Firebase Emulator Hot Reloading
Firebase's emulators only support hot reloading of rules. To achieve hot reloading we use a combination of demo-api's `build-base` target along with the `entr` command, which watches for changes produced by `nx build-base demo-api`. Currently the emulators do not [shut down gracefully](https://github.com/firebase/firebase-tools/issues/3034) and/or communicate they have closed. We use the `./wait-for-ports.sh` script to shut these processes down within the docker container before attempting to restart the emulators. The script waits for about 5 seconds before hard-stopping the processes and waiting for the ports to be released.
Firebase's emulators only support hot reloading of Firebase rules.

To achieve hot reloading we use a combination of demo-api's `build-base` target along with the `entr` command, which watches for changes produced by `nx build-base demo-api`.

Currently the emulators do not [shut down gracefully](https://github.com/firebase/firebase-tools/issues/3034) and/or communicate they have closed. We use the `./wait-for-ports.sh` script to shut these processes down within the docker container before attempting to restart the emulators. The script waits for about 5 seconds before hard-stopping the processes and waiting for the ports to be released.

### Deploying Firebase Functions

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { filterFalsyAnyEmptyValues, objectHasNoKeys } from '@dereekb/util';
import { filterFalsyAndEmptyValues, objectHasNoKeys } from '@dereekb/util';
import { WriteResult } from '../types';
import { FirestoreAccessorIncrementUpdate, FirestoreDocumentDataAccessor } from './accessor';

Expand All @@ -12,7 +12,7 @@ export type IncrementUpdateWithAccessorFunction<T> = (data: FirestoreAccessorInc
*/
export function incrementUpdateWithAccessorFunction<T>(accessor: FirestoreDocumentDataAccessor<T>): IncrementUpdateWithAccessorFunction<T> {
return async (data: FirestoreAccessorIncrementUpdate<T>) => {
const updateData = filterFalsyAnyEmptyValues(data);
const updateData = filterFalsyAndEmptyValues(data);

// Only update
if (!objectHasNoKeys(updateData)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { encodeWebsiteFileLinkToWebsiteLinkEncodedData, WebsiteFileLink } from '@dereekb/model';
import { LatLngString, asGetter, ISO8601DateString, Maybe, modelFieldMapFunctions, objectHasKey, stringTrimFunction, latLngString, passThrough } from '@dereekb/util';
import { isValid } from 'date-fns';
import { FirestoreModelKeyGrantedRoleArrayMap } from '../collection';
import { DocumentSnapshot } from '../types';
import { snapshotConverterFunctions } from './snapshot';
import { firestoreArrayMap, firestoreDate, firestoreObjectArray, firestoreEnum, firestoreMap, firestoreModelKeyGrantedRoleArrayMap, firestoreEnumArray, firestoreUniqueKeyedArray, firestoreUniqueStringArray, firestoreNumber, firestoreSubObject, firestoreEncodedArray, firestoreString, DEFAULT_FIRESTORE_STRING_FIELD_VALUE, firestoreLatLngString, firestoreField, optionalFirestoreDate } from './snapshot.field';
import { firestoreWebsiteFileLinkEncodedArray, firestoreArrayMap, firestoreDate, firestoreObjectArray, firestoreEnum, firestoreMap, firestoreModelKeyGrantedRoleArrayMap, firestoreEnumArray, firestoreUniqueKeyedArray, firestoreUniqueStringArray, firestoreNumber, firestoreSubObject, firestoreEncodedArray, firestoreString, DEFAULT_FIRESTORE_STRING_FIELD_VALUE, firestoreLatLngString, firestoreField, optionalFirestoreDate } from './snapshot.field';

describe('firestoreField()', () => {
const defaultValue = -1;
Expand Down Expand Up @@ -315,13 +316,63 @@ describe('firestoreEncodedArray()', () => {
expect(results[0]).toBe(models[0].key);
});

it('should convert to a deencoded form for each value.', () => {
it('should convert to a decoded form for each value.', () => {
const data = ['a', 'b'];

const results = encodedArrayConfig.from.convert(data);
expect(results.length).toBe(data.length);
expect(results[0].key).toBe(data[0]);
});

describe('declarations', () => {
describe('firestoreWebsiteFileLinkEncodedArray()', () => {
const exampleWithAll: WebsiteFileLink = {
type: 't',
mime: 'test/test',
name: 'test-name',
data: 'https://components.dereekb.com/'
};

interface TestWebsiteFileLinkObject {
l: WebsiteFileLink[];
}

const converter = snapshotConverterFunctions<TestWebsiteFileLinkObject>({
fields: {
l: firestoreWebsiteFileLinkEncodedArray()
}
});

it('should convert to an encoded form for each value.', () => {
const snapshot: DocumentSnapshot<TestWebsiteFileLinkObject> = {
id: '0',
ref: {
id: '0'
} as any,
data() {
return {
l: [encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithAll)]
} as any;
}
};

const result = converter.from(snapshot);
expect(result.l.length).toBe(1);
expect(result.l[0].data).toBe(exampleWithAll.data);
});

it('should convert to a decoded form for each value.', () => {
const data = {
l: [exampleWithAll]
};

const result = converter.to(data);

expect(result.l.length).toBe(data.l.length);
expect(result.l[0]).toBe(encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithAll));
});
});
});
});

describe('firestoreMap()', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { UNKNOWN_WEBSITE_LINK_TYPE, WebsiteLink, GrantedRole } from '@dereekb/model';
import { UNKNOWN_WEBSITE_LINK_TYPE, WebsiteLink, GrantedRole, WebsiteFileLink, EncodedWebsiteFileLink, encodeWebsiteFileLinkToWebsiteLinkEncodedData, decodeWebsiteLinkEncodedDataToWebsiteFileLink } from '@dereekb/model';
import { FirestoreModelKey } from '../collection/collection';
import { DateBlockRange, nowISODateString, toISODateString, toJsDate } from '@dereekb/date';
import {
Expand Down Expand Up @@ -577,6 +577,51 @@ export function firestoreWebsiteLinkArray() {
});
}

// MARK: WebsiteFileLink
export const DEFAULT_WEBSITE_FILE_LINK: WebsiteFileLink = {
data: ''
};

export const assignWebsiteFileLinkFunction = assignValuesToPOJOFunction<WebsiteFileLink>({ keysFilter: ['type', 'name', 'mime'], valueFilter: KeyValueTypleValueFilter.EMPTY });
export const firestoreWebsiteFileLinkAssignFn: MapFunction<WebsiteFileLink, WebsiteFileLink> = (input) => {
const behavior = assignWebsiteFileLinkFunction({ ...DEFAULT_WEBSITE_FILE_LINK }, input);
return behavior;
};

export function firestoreWebsiteFileLink() {
return firestoreField<WebsiteFileLink, WebsiteFileLink>({
default: () => DEFAULT_WEBSITE_FILE_LINK,
fromData: firestoreWebsiteFileLinkAssignFn,
toData: firestoreWebsiteFileLinkAssignFn
});
}

// MARK: WebsiteFileLink Array
/**
* Stores the array of WebsiteFileLink values as an array of objects.
*
* @returns
*/
export function firestoreWebsiteFileLinkObjectArray() {
return firestoreObjectArray({
firestoreField: firestoreWebsiteFileLink()
});
}

/**
* Stores the array of WebsiteFileLink values as an array of EncodedWebsiteFileLink values.
*
* @returns
*/
export function firestoreWebsiteFileLinkEncodedArray() {
return firestoreEncodedArray<WebsiteFileLink, EncodedWebsiteFileLink>({
convert: {
fromData: decodeWebsiteLinkEncodedDataToWebsiteFileLink,
toData: encodeWebsiteFileLinkToWebsiteLinkEncodedData
}
});
}

// MARK: DateBlockRange
export const DEFAULT_DATE_BLOCK_RANGE: DateBlockRange = {
i: 0
Expand Down
1 change: 1 addition & 0 deletions packages/model/src/lib/data/website/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './link';
export * from './link.file';
export * from './link.website';
72 changes: 72 additions & 0 deletions packages/model/src/lib/data/website/link.file.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { decodeWebsiteLinkEncodedDataToWebsiteFileLink, encodeWebsiteFileLinkToWebsiteLinkEncodedData, WebsiteFileLink, websiteFileLinkToWebsiteLink, websiteLinkToWebsiteLinkFile, WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE } from './link.file';

const exampleWithAll: WebsiteFileLink = {
type: 't',
mime: 'test/test',
name: 'test-name',
data: 'https://components.dereekb.com/'
};

const exampleWithOnlyData: WebsiteFileLink = {
data: exampleWithAll.data
};

describe('websiteFileLinkToWebsiteLink', () => {
it('should convert the input to a WebsiteLink', () => {
const result = websiteFileLinkToWebsiteLink(exampleWithAll);
expect(result).toBeDefined();
expect(result.t).toBe(WEBSITE_FILE_LINK_WEBSITE_LINK_TYPE);
expect(result.d).toBe(encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithAll));
});
});

describe('websiteLinkToWebsiteLinkFile', () => {
it('should convert the input to a WebsiteLinkFile', () => {
const encoded = websiteFileLinkToWebsiteLink(exampleWithAll);
const decoded = websiteLinkToWebsiteLinkFile(encoded);
expect(decoded).toBeDefined();

expect(decoded.type).toBe(exampleWithAll.type);
expect(decoded.mime).toBe(exampleWithAll.mime);
expect(decoded.name).toBe(exampleWithAll.name);
expect(decoded.data).toBe(exampleWithAll.data);
});
});

describe('encodeWebsiteFileLinkToWebsiteLinkEncodedData', () => {
it('should encode the link to a string', () => {
const result = encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithAll);

expect(result).toContain(exampleWithAll.type);
expect(result).toContain(exampleWithAll.mime);
expect(result).toContain(exampleWithAll.name);
expect(result).toContain(exampleWithAll.data);
});

it('should encode the data-only link to a string', () => {
const result = encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithOnlyData);
expect(result).toContain(exampleWithAll.data);
});
});

describe('decodeWebsiteLinkEncodedDataToWebsiteFileLink', () => {
it('should decode the link', () => {
const encoded = encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithAll);
const decoded = decodeWebsiteLinkEncodedDataToWebsiteFileLink(encoded);

expect(decoded.type).toBe(exampleWithAll.type);
expect(decoded.mime).toBe(exampleWithAll.mime);
expect(decoded.name).toBe(exampleWithAll.name);
expect(decoded.data).toBe(exampleWithAll.data);
});

it('should decode the data-only link', () => {
const result = encodeWebsiteFileLinkToWebsiteLinkEncodedData(exampleWithOnlyData);
const decoded = decodeWebsiteLinkEncodedDataToWebsiteFileLink(result);

expect(decoded.type).toBeUndefined();
expect(decoded.mime).toBeUndefined();
expect(decoded.name).toBeUndefined();
expect(decoded.data).toBe(exampleWithOnlyData.data);
});
});
Loading

0 comments on commit dc58b5c

Please sign in to comment.