-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move all exported functions to
/api
- Loading branch information
1 parent
b6f64e7
commit 246cc64
Showing
7 changed files
with
269 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import {hash} from 'geokit'; | ||
import {validateLocation} from './validate'; | ||
import {GeoFirestoreTypes} from '../definitions'; | ||
import {findGeoPoint} from '../utils'; | ||
|
||
/** | ||
* Encodes a Firestore Document to be added as a GeoDocument. | ||
* | ||
* @param documentData The document being set. | ||
* @param customKey The key of the document to use as the location. Otherwise we default to `coordinates`. | ||
* @return The document encoded as GeoDocument object. | ||
*/ | ||
export function encodeDocumentAdd( | ||
documentData: GeoFirestoreTypes.DocumentData, | ||
customKey?: string | ||
): GeoFirestoreTypes.GeoDocumentData { | ||
if (Object.prototype.toString.call(documentData) !== '[object Object]') { | ||
throw new Error('document must be an object'); | ||
} | ||
const geopoint = findGeoPoint(documentData, customKey); | ||
return encodeGeoDocument(geopoint, documentData); | ||
} | ||
|
||
/** | ||
* Encodes a Firestore Document to be set as a GeoDocument. | ||
* | ||
* @param documentData A map of the fields and values for the document. | ||
* @param options An object to configure the set behavior. Includes custom key for location in document. | ||
* @return The document encoded as GeoDocument object. | ||
*/ | ||
export function encodeDocumentSet( | ||
documentData: GeoFirestoreTypes.DocumentData, | ||
options?: GeoFirestoreTypes.SetOptions | ||
): GeoFirestoreTypes.GeoDocumentData | GeoFirestoreTypes.DocumentData { | ||
if (Object.prototype.toString.call(documentData) !== '[object Object]') { | ||
throw new Error('document must be an object'); | ||
} | ||
const customKey = options && options.customKey; | ||
const geopoint = findGeoPoint( | ||
documentData, | ||
customKey, | ||
options && (options.merge || !!options.mergeFields) | ||
); | ||
if (geopoint) { | ||
return encodeGeoDocument(geopoint, documentData); | ||
} | ||
return documentData; | ||
} | ||
|
||
/** | ||
* Encodes a Firestore Document to be updated as a GeoDocument. | ||
* | ||
* @param documentData The document being updated. | ||
* @param customKey The key of the document to use as the location. Otherwise we default to `coordinates`. | ||
* @return The document encoded as GeoDocument object. | ||
*/ | ||
export function encodeDocumentUpdate( | ||
documentData: GeoFirestoreTypes.UpdateData, | ||
customKey?: string | ||
): GeoFirestoreTypes.UpdateData { | ||
if (Object.prototype.toString.call(documentData) !== '[object Object]') { | ||
throw new Error('document must be an object'); | ||
} | ||
const geopoint = findGeoPoint(documentData, customKey, true); | ||
if (geopoint) { | ||
documentData = encodeGeoDocument(geopoint, documentData); | ||
} | ||
return documentData; | ||
} | ||
|
||
/** | ||
* Encodes a document with a GeoPoint as a GeoDocument. | ||
* | ||
* @param geopoint The location as a Firestore GeoPoint. | ||
* @param documentData Document to encode. | ||
* @return The document encoded as GeoDocument object. | ||
*/ | ||
export function encodeGeoDocument( | ||
geopoint: GeoFirestoreTypes.cloud.GeoPoint | GeoFirestoreTypes.web.GeoPoint, | ||
documentData: GeoFirestoreTypes.DocumentData | ||
): GeoFirestoreTypes.GeoDocumentData { | ||
validateLocation(geopoint); | ||
const geohash = hash({ | ||
lat: geopoint.latitude, | ||
lng: geopoint.longitude, | ||
}); | ||
return { | ||
...documentData, | ||
g: { | ||
geopoint, | ||
geohash, | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
import {validateHash} from 'geokit'; | ||
import {GeoFirestoreTypes} from '../definitions'; | ||
|
||
/** | ||
* Validates a GeoPoint object and returns a boolean if valid, or throws an error if invalid. | ||
* | ||
* @param location The Firestore GeoPoint to be verified. | ||
* @param flag Tells function to send up boolean if not valid instead of throwing an error. | ||
*/ | ||
export function validateLocation( | ||
location: GeoFirestoreTypes.web.GeoPoint | GeoFirestoreTypes.cloud.GeoPoint, | ||
flag = false | ||
): boolean { | ||
let error: string; | ||
|
||
if (!location) { | ||
error = 'GeoPoint must exist'; | ||
} else if (typeof location.latitude === 'undefined') { | ||
error = 'latitude must exist on GeoPoint'; | ||
} else if (typeof location.longitude === 'undefined') { | ||
error = 'longitude must exist on GeoPoint'; | ||
} else { | ||
const latitude = location.latitude; | ||
const longitude = location.longitude; | ||
|
||
if (typeof latitude !== 'number' || isNaN(latitude)) { | ||
error = 'latitude must be a number'; | ||
} else if (latitude < -90 || latitude > 90) { | ||
error = 'latitude must be within the range [-90, 90]'; | ||
} else if (typeof longitude !== 'number' || isNaN(longitude)) { | ||
error = 'longitude must be a number'; | ||
} else if (longitude < -180 || longitude > 180) { | ||
error = 'longitude must be within the range [-180, 180]'; | ||
} | ||
} | ||
|
||
if (typeof error !== 'undefined' && !flag) { | ||
throw new Error('Invalid location: ' + error); | ||
} else { | ||
return !error; | ||
} | ||
} | ||
|
||
/** | ||
* Validates the inputted limit and throws an error, or returns boolean, if it is invalid. | ||
* | ||
* @param limit The limit to be applied by `GeoQuery.limit()` | ||
* @param flag Tells function to send up boolean if valid instead of throwing an error. | ||
*/ | ||
export function validateLimit(limit: number, flag = false): boolean { | ||
let error: string; | ||
if (typeof limit !== 'number' || isNaN(limit)) { | ||
error = 'limit must be a number'; | ||
} else if (limit < 0) { | ||
error = 'limit must be greater than or equal to 0'; | ||
} | ||
|
||
if (typeof error !== 'undefined' && !flag) { | ||
throw new Error(error); | ||
} else { | ||
return !error; | ||
} | ||
} | ||
|
||
/** | ||
* Validates the inputted GeoDocument object and throws an error, or returns boolean, if it is invalid. | ||
* | ||
* @param documentData The GeoDocument object to be validated. | ||
* @param flag Tells function to send up boolean if valid instead of throwing an error. | ||
* @return Flag if data is valid | ||
*/ | ||
export function validateGeoDocument( | ||
documentData: GeoFirestoreTypes.GeoDocumentData, | ||
flag = false | ||
): boolean { | ||
let error: string; | ||
|
||
if (!documentData) { | ||
error = 'no document found'; | ||
} else if ('g' in documentData) { | ||
error = !validateHash(documentData.g.geohash, true) | ||
? 'invalid geohash on object' | ||
: null; | ||
error = !validateLocation(documentData.g.geopoint, true) | ||
? 'invalid location on object' | ||
: error; | ||
} else { | ||
error = 'no `g` field found in object'; | ||
} | ||
|
||
if (error && !flag) { | ||
throw new Error('Invalid GeoFirestore object: ' + error); | ||
} else { | ||
return !error; | ||
} | ||
} | ||
|
||
/** | ||
* Validates the inputted query criteria and throws an error if it is invalid. | ||
* | ||
* @param newQueryCriteria The criteria which specifies the query's center and/or radius. | ||
* @param requireCenterAndRadius The criteria which center and radius required. | ||
*/ | ||
export function validateQueryCriteria( | ||
newQueryCriteria: GeoFirestoreTypes.QueryCriteria, | ||
requireCenterAndRadius = false | ||
): void { | ||
if (typeof newQueryCriteria !== 'object') { | ||
throw new Error('QueryCriteria must be an object'); | ||
} else if ( | ||
typeof newQueryCriteria.center === 'undefined' && | ||
typeof newQueryCriteria.radius === 'undefined' | ||
) { | ||
throw new Error('radius and/or center must be specified'); | ||
} else if ( | ||
requireCenterAndRadius && | ||
(typeof newQueryCriteria.center === 'undefined' || | ||
typeof newQueryCriteria.radius === 'undefined') | ||
) { | ||
throw new Error( | ||
'QueryCriteria for a new query must contain both a center and a radius' | ||
); | ||
} | ||
|
||
// Throw an error if there are any extraneous attributes | ||
const keys: string[] = Object.keys(newQueryCriteria); | ||
for (const key of keys) { | ||
if (!['center', 'radius', 'limit'].includes(key)) { | ||
throw new Error( | ||
"Unexpected attribute '" + key + "' found in query criteria" | ||
); | ||
} | ||
} | ||
|
||
// Validate the 'center' attribute | ||
if (typeof newQueryCriteria.center !== 'undefined') { | ||
validateLocation(newQueryCriteria.center); | ||
} | ||
|
||
// Validate the 'radius' attribute | ||
if (typeof newQueryCriteria.radius !== 'undefined') { | ||
if ( | ||
typeof newQueryCriteria.radius !== 'number' || | ||
isNaN(newQueryCriteria.radius) | ||
) { | ||
throw new Error('radius must be a number'); | ||
} else if (newQueryCriteria.radius < 0) { | ||
throw new Error('radius must be greater than or equal to 0'); | ||
} | ||
} | ||
|
||
// Validate the 'limit' attribute | ||
if (typeof newQueryCriteria.limit !== 'undefined') { | ||
validateLimit(newQueryCriteria.limit); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
export {geoQueryGet} from './api/query-get'; | ||
export {geoQueryOnSnapshot} from './api/query-on-snapshot'; | ||
export {GeoQuerySnapshot} from './api/snapshot'; | ||
export * from './definitions'; | ||
export { | ||
encodeDocumentAdd, | ||
encodeDocumentSet, | ||
encodeDocumentUpdate, | ||
encodeGeoDocument, | ||
} from './api/encode'; | ||
export {geoQueryGet} from './api/query-get'; | ||
export {geoQueryOnSnapshot} from './api/query-on-snapshot'; | ||
export {GeoQuerySnapshot} from './api/snapshot'; | ||
export { | ||
validateLocation, | ||
validateLimit, | ||
validateGeoDocument, | ||
validateQueryCriteria, | ||
} from './utils'; | ||
} from './api/validate'; | ||
export * from './definitions'; |
Oops, something went wrong.