-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0abfe58
commit 464d757
Showing
16 changed files
with
480 additions
and
9 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 @@ | ||
{ "releases": [{ "name": "@keystone-alpha/fields", "type": "minor" }], "dependents": [] } |
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 @@ | ||
Add Location field |
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,18 @@ | ||
{ | ||
"releases": [{ "name": "@arch-ui/select", "type": "minor" }], | ||
"dependents": [ | ||
{ | ||
"name": "@keystone-alpha/app-admin-ui", | ||
"type": "patch", | ||
"dependencies": ["@keystone-alpha/fields", "@arch-ui/select"] | ||
}, | ||
{ "name": "@arch-ui/docs", "type": "patch", "dependencies": ["@arch-ui/select"] }, | ||
{ "name": "@arch-ui/day-picker", "type": "patch", "dependencies": ["@arch-ui/select"] }, | ||
{ | ||
"name": "@keystone-alpha/fields", | ||
"type": "patch", | ||
"dependencies": ["@arch-ui/day-picker", "@arch-ui/select"] | ||
}, | ||
{ "name": "@keystone-alpha/website", "type": "patch", "dependencies": ["@arch-ui/select"] } | ||
] | ||
} |
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 @@ | ||
Allow async and creatable react-selects |
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
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,165 @@ | ||
import { Implementation } from '../../Implementation'; | ||
import { MongooseFieldAdapter } from '@keystone-alpha/adapter-mongoose'; | ||
import { KnexFieldAdapter } from '@keystone-alpha/adapter-knex'; | ||
import mongoose from 'mongoose'; | ||
|
||
import fetch from 'node-fetch'; | ||
|
||
// Disabling the getter of mongoose >= 5.1.0 | ||
// https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md#checking-if-a-path-is-populated | ||
mongoose.set('objectIdGetter', false); | ||
|
||
const { | ||
Types: { ObjectId }, | ||
} = mongoose; | ||
|
||
export class Location extends Implementation { | ||
constructor(_, { googleMapsKey }) { | ||
super(...arguments); | ||
this.graphQLOutputType = 'Location'; | ||
|
||
if (!googleMapsKey) { | ||
throw new Error( | ||
'You must provide a `googleMapsKey` to Location Field. To generate a Google Maps API please visit: https://developers.google.com/maps/documentation/javascript/get-api-key' | ||
); | ||
} | ||
|
||
this._googleMapsKey = googleMapsKey; | ||
} | ||
|
||
extendAdminMeta(meta) { | ||
return { | ||
...meta, | ||
googleMapsKey: this._googleMapsKey, | ||
}; | ||
} | ||
|
||
gqlOutputFields() { | ||
return [`${this.path}: ${this.graphQLOutputType}`]; | ||
} | ||
|
||
gqlQueryInputFields() { | ||
return [ | ||
...this.equalityInputFields('String'), | ||
...this.stringInputFields('String'), | ||
...this.inInputFields('String'), | ||
]; | ||
} | ||
|
||
getGqlAuxTypes() { | ||
return [ | ||
` | ||
type ${this.graphQLOutputType} { | ||
id: ID | ||
googlePlaceID: String | ||
formattedAddress: String | ||
lat: Float | ||
lng: Float | ||
} | ||
`, | ||
]; | ||
} | ||
|
||
// Called on `User.avatar` for example | ||
gqlOutputFieldResolvers() { | ||
return { | ||
[this.path]: item => { | ||
const itemValues = item[this.path]; | ||
if (!itemValues) { | ||
return null; | ||
} | ||
return itemValues; | ||
}, | ||
}; | ||
} | ||
|
||
async resolveInput({ resolvedData }) { | ||
const placeId = resolvedData[this.path]; | ||
|
||
// NOTE: The following two conditions could easily be combined into a | ||
// single `if (!inputId) return inputId`, but that would lose the nuance of | ||
// returning `undefined` vs `null`. | ||
// Premature Optimisers; be ware! | ||
if (typeof placeId === 'undefined') { | ||
// Nothing was passed in, so we can bail early. | ||
return undefined; | ||
} | ||
|
||
if (placeId === null) { | ||
// `null` was specifically set, and we should set the field value to null | ||
// To do that we... return `null` | ||
return null; | ||
} | ||
|
||
const response = await fetch( | ||
`https://maps.googleapis.com/maps/api/geocode/json?place_id=${placeId}&key=${this._googleMapsKey}` | ||
).then(r => r.json()); | ||
|
||
if (response.results && response.results[0]) { | ||
const { place_id, formatted_address } = response.results[0]; | ||
const { lat, lng } = response.results[0].geometry.location; | ||
return { | ||
id: new ObjectId(), | ||
googlePlaceID: place_id, | ||
formattedAddress: formatted_address, | ||
lat: lat, | ||
lng: lng, | ||
}; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
get gqlUpdateInputFields() { | ||
return [`${this.path}: String`]; | ||
} | ||
|
||
get gqlCreateInputFields() { | ||
return [`${this.path}: String`]; | ||
} | ||
} | ||
|
||
const CommonLocationInterface = superclass => | ||
class extends superclass { | ||
getQueryConditions(dbPath) { | ||
return { | ||
...this.equalityConditions(dbPath), | ||
...this.stringConditions(dbPath), | ||
...this.inConditions(dbPath), | ||
}; | ||
} | ||
}; | ||
|
||
export class MongoLocationInterface extends CommonLocationInterface(MongooseFieldAdapter) { | ||
addToMongooseSchema(schema) { | ||
const schemaOptions = { | ||
type: { | ||
id: ObjectId, | ||
googlePlaceID: String, | ||
formattedAddress: String, | ||
lat: Number, | ||
lng: Number, | ||
}, | ||
}; | ||
schema.add({ [this.path]: this.mergeSchemaOptions(schemaOptions, this.config) }); | ||
} | ||
} | ||
|
||
export class KnexLocationInterface extends CommonLocationInterface(KnexFieldAdapter) { | ||
constructor() { | ||
super(...arguments); | ||
|
||
// Error rather than ignoring invalid config | ||
// We totally can index these values, it's just not trivial. See issue #1297 | ||
if (this.config.isUnique || this.config.isIndexed) { | ||
throw `The Location field type doesn't support indexes on Knex. ` + | ||
`Check the config for ${this.path} on the ${this.field.listKey} list`; | ||
} | ||
} | ||
|
||
addToTableSchema(table) { | ||
const column = table.jsonb(this.path); | ||
if (this.isNotNullable) column.notNullable(); | ||
if (this.defaultTo) column.defaultTo(this.defaultTo); | ||
} | ||
} |
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 @@ | ||
<!--[meta] | ||
section: api | ||
subSection: field-types | ||
title: Location | ||
[meta]--> | ||
|
||
# Location | ||
|
||
The Location Field Type enables storing data from the Google Maps API. | ||
|
||
## Usage | ||
|
||
```javascript | ||
const { Location } = require('@keystone-alpha/fields'); | ||
const { Keystone } = require('@keystone-alpha/keystone'); | ||
|
||
const keystone = new Keystone(/* ... */); | ||
|
||
keystone.createList('Event', { | ||
fields: { | ||
venue: { | ||
type: Location, | ||
googleMapsKey: 'GOOGLE_MAPS_KEY', | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
## GraphQL | ||
|
||
**Query** | ||
|
||
```graphql | ||
query { | ||
allEvents { | ||
venue { | ||
id | ||
googlePlaceID | ||
formattedAddress | ||
lat | ||
lng | ||
} | ||
} | ||
} | ||
|
||
# Result: | ||
|
||
# { | ||
# "data": { | ||
# "allEvents": [ | ||
# { | ||
# "venue": { | ||
# "id": "1", | ||
# googlePlaceID: "ChIJOza7MD-uEmsRrf4t12uji6Y", | ||
# "formattedAddress": "10/191 Clarence St, Sydney NSW 2000, Australia", | ||
# "lat": -33.869374, | ||
# "lng": 151.205097 | ||
# } | ||
# } | ||
# ] | ||
# } | ||
# } | ||
``` | ||
|
||
### Mutations | ||
|
||
To create a `Location`, pass the Google `place_id` for the desired field path. | ||
|
||
```graphql | ||
mutation { | ||
createEvent(data: { venue: "ChIJOza7MD-uEmsRrf4t12uji6Y" }) { | ||
venue { | ||
id | ||
googlePlaceID | ||
formattedAddress | ||
lat | ||
lng | ||
} | ||
} | ||
} | ||
|
||
# Result: | ||
# { | ||
# "createEvent": { | ||
# "venue": { | ||
# "id": "1", | ||
# googlePlaceID: "ChIJOza7MD-uEmsRrf4t12uji6Y", | ||
# "formattedAddress": "10/191 Clarence St, Sydney NSW 2000, Australia", | ||
# "lat": -33.869374, | ||
# "lng": 151.205097 | ||
# } | ||
# } | ||
# } | ||
``` |
Oops, something went wrong.