EV Chargers service (#219)

This commit is contained in:
Dominic Wong
2021-10-01 14:17:54 +01:00
committed by GitHub
parent 3c38d96881
commit f52cfba017
38 changed files with 14584 additions and 3 deletions

View File

@@ -0,0 +1,238 @@
package evchargers
import (
"github.com/m3o/m3o-go/client"
)
func NewEvchargersService(token string) *EvchargersService {
return &EvchargersService{
client: client.NewClient(&client.Options{
Token: token,
}),
}
}
type EvchargersService struct {
client *client.Client
}
// Retrieve reference data as used by this API
func (t *EvchargersService) ReferenceData(request *ReferenceDataRequest) (*ReferenceDataResponse, error) {
rsp := &ReferenceDataResponse{}
return rsp, t.client.Call("evchargers", "ReferenceData", request, rsp)
}
// Search by giving a coordinate and a max distance, or bounding box and optional filters
func (t *EvchargersService) Search(request *SearchRequest) (*SearchResponse, error) {
rsp := &SearchResponse{}
return rsp, t.client.Call("evchargers", "Search", request, rsp)
}
type Address struct {
// Any comments about how to access the charger
AccessComments string `json:"accessComments"`
AddressLine1 string `json:"addressLine1"`
AddressLine2 string `json:"addressLine2"`
Country *Country `json:"country"`
CountryId string `json:"countryId"`
Location *Coordinates `json:"location"`
Postcode string `json:"postcode"`
StateOrProvince string `json:"stateOrProvince"`
Title string `json:"title"`
Town string `json:"town"`
}
type BoundingBox struct {
BottomLeft *Coordinates `json:"bottomLeft"`
TopRight *Coordinates `json:"topRight"`
}
type ChargerType struct {
Comments string `json:"comments"`
Id string `json:"id"`
// Is this 40KW+
IsFastChargeCapable bool `json:"isFastChargeCapable"`
Title string `json:"title"`
}
type CheckinStatusType struct {
Id string `json:"id"`
IsAutomated bool `json:"isAutomated"`
IsPositive bool `json:"isPositive"`
Title string `json:"title"`
}
type Connection struct {
// The amps offered
Amps float64 `json:"amps"`
ConnectionType *ConnectionType `json:"connectionType"`
// The ID of the connection type
ConnectionTypeId string `json:"connectionTypeId"`
// The current
Current string `json:"current"`
// The level of charging power available
Level string `json:"level"`
// The power in KW
Power float64 `json:"power"`
Reference string `json:"reference"`
// The voltage offered
Voltage float64 `json:"voltage"`
}
type ConnectionType struct {
FormalName string `json:"formalName"`
Id string `json:"id"`
IsDiscontinued bool `json:"isDiscontinued"`
IsObsolete bool `json:"isObsolete"`
Title string `json:"title"`
}
type Coordinates struct {
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
}
type Country struct {
ContinentCode string `json:"continentCode"`
Id string `json:"id"`
IsoCode string `json:"isoCode"`
Title string `json:"title"`
}
type CurrentType struct {
Description string `json:"description"`
Id string `json:"id"`
Title string `json:"title"`
}
type DataProvider struct {
Comments string `json:"comments"`
DataProviderStatusType *DataProviderStatusType `json:"dataProviderStatusType"`
Id string `json:"id"`
// How is this data licensed
License string `json:"license"`
Title string `json:"title"`
Website string `json:"website"`
}
type DataProviderStatusType struct {
Id string `json:"id"`
IsProviderEnabled bool `json:"isProviderEnabled"`
Title string `json:"title"`
}
type Operator struct {
Comments string `json:"comments"`
ContactEmail string `json:"contactEmail"`
FaultReportEmail string `json:"faultReportEmail"`
Id string `json:"id"`
// Is this operator a private individual vs a company
IsPrivateIndividual bool `json:"isPrivateIndividual"`
PhonePrimary string `json:"phonePrimary"`
PhoneSecondary string `json:"phoneSecondary"`
Title string `json:"title"`
Website string `json:"website"`
}
type Poi struct {
// The address
Address *Address `json:"address"`
// The connections available at this charge point
Connections []Connection `json:"connections"`
// The cost of charging
Cost string `json:"cost"`
// The ID of the data provider
DataProviderId string `json:"dataProviderId"`
// The ID of the charger
Id string `json:"id"`
// The number of charging points
NumPoints int64 `json:"numPoints,string"`
// The operator
Operator *Operator `json:"operator"`
// The ID of the operator of the charger
OperatorId string `json:"operatorId"`
// The type of usage
UsageType *UsageType `json:"usageType"`
// The type of usage for this charger point (is it public, membership required, etc)
UsageTypeId string `json:"usageTypeId"`
}
type ReferenceDataRequest struct {
}
type ReferenceDataResponse struct {
// The types of charger
ChargerTypes *ChargerType `json:"chargerTypes"`
// The types of checkin status
CheckinStatusTypes *CheckinStatusType `json:"checkinStatusTypes"`
// The types of connection
ConnectionTypes *ConnectionType `json:"connectionTypes"`
// The countries
Countries []Country `json:"countries"`
// The types of current
CurrentTypes *CurrentType `json:"currentTypes"`
// The providers of the charger data
DataProviders *DataProvider `json:"dataProviders"`
// The companies operating the chargers
Operators []Operator `json:"operators"`
// The status of the charger
StatusTypes *StatusType `json:"statusTypes"`
// The status of a submission
SubmissionStatusTypes *SubmissionStatusType `json:"submissionStatusTypes"`
// The different types of usage
UsageTypes *UsageType `json:"usageTypes"`
// The types of user comment
UserCommentTypes *UserCommentType `json:"userCommentTypes"`
}
type SearchRequest struct {
// Bounding box to search within (top left and bottom right coordinates)
Box *BoundingBox `json:"box"`
// IDs of the connection type
ConnectionTypes string `json:"connectionTypes"`
// Country ID
CountryId string `json:"countryId"`
// Search distance from point in metres, defaults to 5000m
Distance int64 `json:"distance,string"`
// Supported charging levels
Levels []string `json:"levels"`
// Coordinates from which to begin search
Location *Coordinates `json:"location"`
// Maximum number of results to return, defaults to 100
MaxResults int64 `json:"maxResults,string"`
// Minimum power in KW. Note: data not available for many chargers
MinPower int64 `json:"minPower,string"`
// IDs of the the EV charger operator
Operators []string `json:"operators"`
// Usage of the charge point (is it public, membership required, etc)
UsageTypes string `json:"usageTypes"`
}
type SearchResponse struct {
Pois []Poi `json:"pois"`
}
type StatusType struct {
Id string `json:"id"`
IsOperational bool `json:"isOperational"`
Title string `json:"title"`
}
type SubmissionStatusType struct {
Id string `json:"id"`
IsLive bool `json:"isLive"`
Title string `json:"title"`
}
type UsageType struct {
Id string `json:"id"`
IsAccessKeyRequired bool `json:"isAccessKeyRequired"`
IsMembershipRequired bool `json:"isMembershipRequired"`
IsPayAtLocation bool `json:"isPayAtLocation"`
Title string `json:"title"`
}
type UserCommentType struct {
Id string `json:"id"`
Title string `json:"title"`
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/micro/services/clients/go/db"
"github.com/micro/services/clients/go/email"
"github.com/micro/services/clients/go/emoji"
"github.com/micro/services/clients/go/evchargers"
"github.com/micro/services/clients/go/file"
"github.com/micro/services/clients/go/forex"
"github.com/micro/services/clients/go/geocoding"
@@ -51,6 +52,7 @@ func NewClient(token string) *Client {
DbService: db.NewDbService(token),
EmailService: email.NewEmailService(token),
EmojiService: emoji.NewEmojiService(token),
EvchargersService: evchargers.NewEvchargersService(token),
FileService: file.NewFileService(token),
ForexService: forex.NewForexService(token),
GeocodingService: geocoding.NewGeocodingService(token),
@@ -93,6 +95,7 @@ type Client struct {
DbService *db.DbService
EmailService *email.EmailService
EmojiService *emoji.EmojiService
EvchargersService *evchargers.EvchargersService
FileService *file.FileService
ForexService *forex.ForexService
GeocodingService *geocoding.GeocodingService

233
clients/ts/evchargers/index.ts Executable file
View File

@@ -0,0 +1,233 @@
import * as m3o from "@m3o/m3o-node";
export class EvchargersService {
private client: m3o.Client;
constructor(token: string) {
this.client = new m3o.Client({ token: token });
}
// Retrieve reference data as used by this API
referenceData(request: ReferenceDataRequest): Promise<ReferenceDataResponse> {
return this.client.call(
"evchargers",
"ReferenceData",
request
) as Promise<ReferenceDataResponse>;
}
// Search by giving a coordinate and a max distance, or bounding box and optional filters
search(request: SearchRequest): Promise<SearchResponse> {
return this.client.call(
"evchargers",
"Search",
request
) as Promise<SearchResponse>;
}
}
export interface Address {
// Any comments about how to access the charger
accessComments?: string;
addressLine1?: string;
addressLine2?: string;
country?: { [key: string]: any };
countryId?: string;
location?: Coordinates;
postcode?: string;
stateOrProvince?: string;
title?: string;
town?: string;
}
export interface BoundingBox {
bottomLeft?: Coordinates;
topRight?: Coordinates;
}
export interface ChargerType {
comments?: string;
id?: string;
// Is this 40KW+
isFastChargeCapable?: boolean;
title?: string;
}
export interface CheckinStatusType {
id?: string;
isAutomated?: boolean;
isPositive?: boolean;
title?: string;
}
export interface Connection {
// The amps offered
amps?: number;
connectionType?: ConnectionType;
// The ID of the connection type
connectionTypeId?: string;
// The current
current?: string;
// The level of charging power available
level?: string;
// The power in KW
power?: number;
reference?: string;
// The voltage offered
voltage?: number;
}
export interface ConnectionType {
formalName?: string;
id?: string;
isDiscontinued?: boolean;
isObsolete?: boolean;
title?: string;
}
export interface Coordinates {
latitude?: number;
longitude?: number;
}
export interface Country {
continentCode?: string;
id?: string;
isoCode?: string;
title?: string;
}
export interface CurrentType {
description?: string;
id?: string;
title?: string;
}
export interface DataProvider {
comments?: string;
dataProviderStatusType?: DataProviderStatusType;
id?: string;
// How is this data licensed
license?: string;
title?: string;
website?: string;
}
export interface DataProviderStatusType {
id?: string;
isProviderEnabled?: boolean;
title?: string;
}
export interface Operator {
comments?: string;
contactEmail?: string;
faultReportEmail?: string;
id?: string;
// Is this operator a private individual vs a company
isPrivateIndividual?: boolean;
phonePrimary?: string;
phoneSecondary?: string;
title?: string;
website?: string;
}
export interface Poi {
// The address
address?: { [key: string]: any };
// The connections available at this charge point
connections?: Connection[];
// The cost of charging
cost?: string;
// The ID of the data provider
dataProviderId?: string;
// The ID of the charger
id?: string;
// The number of charging points
numPoints?: number;
// The operator
operator?: { [key: string]: any };
// The ID of the operator of the charger
operatorId?: string;
// The type of usage
usageType?: UsageType;
// The type of usage for this charger point (is it public, membership required, etc)
usageTypeId?: string;
}
export interface ReferenceDataRequest {}
export interface ReferenceDataResponse {
// The types of charger
chargerTypes?: ChargerType;
// The types of checkin status
checkinStatusTypes?: CheckinStatusType;
// The types of connection
connectionTypes?: ConnectionType;
// The countries
countries?: Country[];
// The types of current
currentTypes?: CurrentType;
// The providers of the charger data
dataProviders?: DataProvider;
// The companies operating the chargers
operators?: Operator[];
// The status of the charger
statusTypes?: StatusType;
// The status of a submission
submissionStatusTypes?: SubmissionStatusType;
// The different types of usage
usageTypes?: UsageType;
// The types of user comment
userCommentTypes?: UserCommentType;
}
export interface SearchRequest {
// Bounding box to search within (top left and bottom right coordinates)
box?: BoundingBox;
// IDs of the connection type
connectionTypes?: string;
// Country ID
countryId?: string;
// Search distance from point in metres, defaults to 5000m
distance?: number;
// Supported charging levels
levels?: string[];
// Coordinates from which to begin search
location?: Coordinates;
// Maximum number of results to return, defaults to 100
maxResults?: number;
// Minimum power in KW. Note: data not available for many chargers
minPower?: number;
// IDs of the the EV charger operator
operators?: string[];
// Usage of the charge point (is it public, membership required, etc)
usageTypes?: string;
}
export interface SearchResponse {
pois?: Poi[];
}
export interface StatusType {
id?: string;
isOperational?: boolean;
title?: string;
}
export interface SubmissionStatusType {
id?: string;
isLive?: boolean;
title?: string;
}
export interface UsageType {
id?: string;
isAccessKeyRequired?: boolean;
isMembershipRequired?: boolean;
isPayAtLocation?: boolean;
title?: string;
}
export interface UserCommentType {
id?: string;
title?: string;
}

View File

@@ -6,6 +6,7 @@ import * as currency from "./currency";
import * as db from "./db";
import * as email from "./email";
import * as emoji from "./emoji";
import * as evchargers from "./evchargers";
import * as file from "./file";
import * as forex from "./forex";
import * as geocoding from "./geocoding";
@@ -45,6 +46,7 @@ export class Client {
this.dbService = new db.DbService(token);
this.emailService = new email.EmailService(token);
this.emojiService = new emoji.EmojiService(token);
this.evchargersService = new evchargers.EvchargersService(token);
this.fileService = new file.FileService(token);
this.forexService = new forex.ForexService(token);
this.geocodingService = new geocoding.GeocodingService(token);
@@ -83,6 +85,7 @@ export class Client {
dbService: db.DbService;
emailService: email.EmailService;
emojiService: emoji.EmojiService;
evchargersService: evchargers.EvchargersService;
fileService: file.FileService;
forexService: forex.ForexService;
geocodingService: geocoding.GeocodingService;

View File

@@ -17,6 +17,7 @@
"./db": "./dist/db/index.js",
"./email": "./dist/email/index.js",
"./emoji": "./dist/emoji/index.js",
"./evchargers": "./dist/evchargers/index.js",
"./file": "./dist/file/index.js",
"./forex": "./dist/forex/index.js",
"./geocoding": "./dist/geocoding/index.js",
@@ -62,5 +63,5 @@
},
"type": "module",
"types": "dist/index.d.ts",
"version": "1.0.531"
"version": "1.0.532"
}

View File

@@ -140,7 +140,6 @@ func schemaToGoExample(serviceName, typeName string, schemas map[string]*openapi
continue
}
}
switch v.Value.Type {
case "object":
typ, found := detectType(k, v.Value.Properties)
@@ -162,9 +161,15 @@ func schemaToGoExample(serviceName, typeName string, schemas map[string]*openapi
if found {
ret += k + fieldSeparator + arrayPrefix + serviceName + "." + strings.Title(typ) + objectOpen + serviceName + "." + strings.Title(typ) + objectOpen + recurse(v.Value.Items.Value.Properties, append(append(path, k), "[0]")) + objectClose + objectClose + arrayPostfix + fieldDelimiter
} else {
arrint := val.([]interface{})
switch v.Value.Items.Value.Type {
case "string":
ret += k + fieldSeparator + arrayPrefix + fmt.Sprintf("\"%v\"", val) + arrayPostfix + fieldDelimiter
arrstr := make([]string, len(arrint))
for i, v := range arrint {
arrstr[i] = fmt.Sprintf("%v", v)
}
ret += k + fieldSeparator + fmt.Sprintf("%#v", arrstr) + fieldDelimiter
case "number", "boolean":
ret += k + fieldSeparator + arrayPrefix + fmt.Sprintf("%v", val) + arrayPostfix + fieldDelimiter
case "object":

2
evchargers/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
evchargers

3
evchargers/Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM alpine
ADD evchargers /evchargers
ENTRYPOINT [ "/evchargers" ]

28
evchargers/Makefile Normal file
View File

@@ -0,0 +1,28 @@
GOPATH:=$(shell go env GOPATH)
.PHONY: init
init:
go get -u github.com/golang/protobuf/proto
go get -u github.com/golang/protobuf/protoc-gen-go
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
go get github.com/micro/micro/v3/cmd/protoc-gen-openapi
.PHONY: api
api:
protoc --openapi_out=. --proto_path=. proto/evchargers.proto
.PHONY: proto
proto:
protoc --proto_path=. --micro_out=. --go_out=:. proto/evchargers.proto
.PHONY: build
build:
go build -o evchargers *.go
.PHONY: test
test:
go test -v ./... -cover
.PHONY: docker
docker:
docker build . -t evchargers:latest

11
evchargers/README.md Normal file
View File

@@ -0,0 +1,11 @@
Find electric vehicle (EV) chargers wherever you go
# EV Chargers Service
This is the E(lectric) V(ehicle) chargers API. Search for EV chargers using
- location and distance
- bounding box
and filter the results based on connection type, operator, and more.
Powered by https://openchargemap.org/

643
evchargers/examples.json Normal file
View File

@@ -0,0 +1,643 @@
{
"search": [
{
"title": "Search by location",
"run_check": true,
"request": {
"location": {
"latitude": 51.53336351319885,
"longitude": -0.0252
},
"distance": 2000,
"max_results": 2
},
"response": {
"pois": [
{
"id": "121442",
"data_provider_id": "1",
"operator_id": "25",
"usage_type_id": "4",
"address": {
"location": {
"latitude": 51.539715,
"longitude": -0.02769
},
"title": "St Marks Gate",
"address_line_1": "Opposite 1 St Marks Gate",
"address_line_2": "",
"town": "London",
"state_or_province": "",
"access_comments": "",
"postcode": "E9 5HT",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "1",
"reference": "",
"level": "2",
"amps": 16,
"voltage": 230,
"power": 4,
"current": "10",
"connection_type": {
"id": "1",
"title": "Type 1 (J1772)",
"formal_name": "SAE J1772-2009",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "25",
"reference": "",
"level": "2",
"amps": 32,
"voltage": 230,
"power": 7,
"current": "10",
"connection_type": {
"id": "25",
"title": "Type 2 (Socket Only)",
"formal_name": "IEC 62196-2 Type 2",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "1",
"reference": "",
"level": "2",
"amps": 16,
"voltage": 230,
"power": 4,
"current": "10",
"connection_type": {
"id": "1",
"title": "Type 1 (J1772)",
"formal_name": "SAE J1772-2009",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "25",
"reference": "",
"level": "2",
"amps": 32,
"voltage": 230,
"power": 7,
"current": "10",
"connection_type": {
"id": "25",
"title": "Type 2 (Socket Only)",
"formal_name": "IEC 62196-2 Type 2",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "1",
"reference": "",
"level": "2",
"amps": 16,
"voltage": 230,
"power": 4,
"current": "10",
"connection_type": {
"id": "1",
"title": "Type 1 (J1772)",
"formal_name": "SAE J1772-2009",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "25",
"reference": "",
"level": "2",
"amps": 32,
"voltage": 230,
"power": 7,
"current": "10",
"connection_type": {
"id": "25",
"title": "Type 2 (Socket Only)",
"formal_name": "IEC 62196-2 Type 2",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "3",
"cost": "£0.059/minute; min £1.18; other tariffs available",
"operator": {
"id": "25",
"title": "Source London",
"website": "http://www.sourcelondon.net/",
"comments": "Monthly post-payment membership system, with per-minute billing and optional lower tariffs in return for monthly subscription payment.",
"is_private_individual": false,
"contact_email": "membership@sourcelondon.net",
"phone_primary": "",
"phone_secondary": "",
"fault_report_email": "membership@sourcelondon.net"
},
"usage_type": {
"id": "4",
"title": "Public - Membership Required",
"is_pay_at_location": false,
"is_membership_required": true,
"is_access_key_required": true
}
},
{
"id": "189330",
"data_provider_id": "1",
"operator_id": "3296",
"usage_type_id": "5",
"address": {
"location": {
"latitude": 51.529694,
"longitude": -0.015285153
},
"title": "McDonald's Bow",
"address_line_1": "4 Payne Road",
"address_line_2": "",
"town": "London",
"state_or_province": "England",
"access_comments": "",
"postcode": "E3 2SP",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "33",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "33",
"title": "CCS (Type 2)",
"formal_name": "IEC 62196-3 Configuration FF",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "2",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "2",
"title": "CHAdeMO",
"formal_name": "IEC 62196-3 Configuration AA",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "2",
"cost": "£0.40/kWh",
"operator": {
"id": "3296",
"title": "InstaVolt Ltd",
"website": "http://instavolt.co.uk/",
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
"is_private_individual": false,
"contact_email": "",
"phone_primary": "",
"phone_secondary": "",
"fault_report_email": ""
},
"usage_type": {
"id": "5",
"title": "Public - Pay At Location",
"is_pay_at_location": true,
"is_membership_required": false,
"is_access_key_required": false
}
}
]
}
},
{
"title": "Search by bounding box",
"run_check": true,
"request": {
"box": {
"bottom_left": {
"latitude":51.52627543859447,
"longitude": -0.03635349400295168
},
"top_right": {
"latitude": 51.56717121807993,
"longitude":-0.002293530559768285
}
},
"max_results": 2
},
"response": {
"pois": [
{
"id": "189330",
"data_provider_id": "1",
"operator_id": "3296",
"usage_type_id": "5",
"address": {
"location": {
"latitude": 51.529694,
"longitude": -0.015285153
},
"title": "McDonald's Bow",
"address_line_1": "4 Payne Road",
"address_line_2": "",
"town": "London",
"state_or_province": "England",
"access_comments": "",
"postcode": "E3 2SP",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "33",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "33",
"title": "CCS (Type 2)",
"formal_name": "IEC 62196-3 Configuration FF",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "2",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "2",
"title": "CHAdeMO",
"formal_name": "IEC 62196-3 Configuration AA",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "2",
"cost": "£0.40/kWh",
"operator": {
"id": "3296",
"title": "InstaVolt Ltd",
"website": "http://instavolt.co.uk/",
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
"is_private_individual": false,
"contact_email": "",
"phone_primary": "",
"phone_secondary": "",
"fault_report_email": ""
},
"usage_type": {
"id": "5",
"title": "Public - Pay At Location",
"is_pay_at_location": true,
"is_membership_required": false,
"is_access_key_required": false
}
},
{
"id": "187765",
"data_provider_id": "18",
"operator_id": "3",
"usage_type_id": "1",
"address": {
"location": {
"latitude": 51.529995,
"longitude": -0.009934
},
"title": "Hunt's Lane",
"address_line_1": "Hunt's Lane",
"address_line_2": "",
"town": "London",
"state_or_province": "",
"access_comments": "",
"postcode": "E15 2QD",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "25",
"reference": "0",
"level": "2",
"amps": 32,
"voltage": 230,
"power": 7,
"current": "10",
"connection_type": {
"id": "25",
"title": "Type 2 (Socket Only)",
"formal_name": "IEC 62196-2 Type 2",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "0",
"cost": "",
"operator": {
"id": "3",
"title": "POD Point (UK)",
"website": "http://www.pod-point.com/",
"comments": "Part of Groupe EDF",
"is_private_individual": false,
"contact_email": "enquiries@pod-point.com",
"phone_primary": "020 7247 4114",
"phone_secondary": "",
"fault_report_email": "enquiries@pod-point.com"
},
"usage_type": {
"id": "1",
"title": "Public",
"is_pay_at_location": false,
"is_membership_required": false,
"is_access_key_required": false
}
}
]
}
},
{
"title": "Search with filters. Fast chargers only",
"run_check": true,
"request": {
"location": {
"latitude": 51.53336351319885,
"longitude": -0.0252
},
"distance": 2000,
"max_results": 2,
"levels": [
"3"
]
},
"response": {
"pois": [
{
"id": "189330",
"data_provider_id": "1",
"operator_id": "3296",
"usage_type_id": "5",
"address": {
"location": {
"latitude": 51.529694,
"longitude": -0.015285153
},
"title": "McDonald's Bow",
"address_line_1": "4 Payne Road",
"address_line_2": "",
"town": "London",
"state_or_province": "England",
"access_comments": "",
"postcode": "E3 2SP",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "33",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "33",
"title": "CCS (Type 2)",
"formal_name": "IEC 62196-3 Configuration FF",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "2",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "2",
"title": "CHAdeMO",
"formal_name": "IEC 62196-3 Configuration AA",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "2",
"cost": "£0.40/kWh",
"operator": {
"id": "3296",
"title": "InstaVolt Ltd",
"website": "http://instavolt.co.uk/",
"comments": "Pay-as-you-go, charged per kWh, contactless bank card payment.",
"is_private_individual": false,
"contact_email": "",
"phone_primary": "",
"phone_secondary": "",
"fault_report_email": ""
},
"usage_type": {
"id": "5",
"title": "Public - Pay At Location",
"is_pay_at_location": true,
"is_membership_required": false,
"is_access_key_required": false
}
},
{
"id": "127251",
"data_provider_id": "1",
"operator_id": "32",
"usage_type_id": "4",
"address": {
"location": {
"latitude": 51.543983,
"longitude": -0.01991
},
"title": "Queen Elizabeth Olympic Park",
"address_line_1": "Queen Elizabeth Olympic Park",
"address_line_2": "",
"town": "Copper Box Car Park",
"state_or_province": "London",
"access_comments": "",
"postcode": "E20 2ST",
"country_id": "1",
"country": {
"id": "1",
"title": "United Kingdom",
"iso_code": "GB",
"continent_code": "EU"
}
},
"connections": [
{
"connection_type_id": "25",
"reference": "",
"level": "2",
"amps": 32,
"voltage": 400,
"power": 22,
"current": "20",
"connection_type": {
"id": "25",
"title": "Type 2 (Socket Only)",
"formal_name": "IEC 62196-2 Type 2",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "2",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "2",
"title": "CHAdeMO",
"formal_name": "IEC 62196-3 Configuration AA",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "33",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 50,
"current": "30",
"connection_type": {
"id": "33",
"title": "CCS (Type 2)",
"formal_name": "IEC 62196-3 Configuration FF",
"is_discontinued": false,
"is_obsolete": false
}
},
{
"connection_type_id": "1036",
"reference": "",
"level": "3",
"amps": 0,
"voltage": 0,
"power": 43,
"current": "20",
"connection_type": {
"id": "1036",
"title": "Type 2 (Tethered Connector) ",
"formal_name": "IEC 62196-2",
"is_discontinued": false,
"is_obsolete": false
}
}
],
"num_points": "2",
"cost": "Inclusive; for Polar Plus subscription members only",
"operator": {
"id": "32",
"title": "BP Pulse",
"website": "https://www.bppulse.co.uk/",
"comments": "Formerly known as Polar Network, or BP Chargemaster",
"is_private_individual": false,
"contact_email": "polarenquiries@chargemasterplc.com",
"phone_primary": "01582 400331",
"phone_secondary": "",
"fault_report_email": "polarenquiries@chargemasterplc.com"
},
"usage_type": {
"id": "4",
"title": "Public - Membership Required",
"is_pay_at_location": false,
"is_membership_required": true,
"is_access_key_required": true
}
}
]
}
}
],
"referenceData": [
{
"title": "Get reference data",
"run_check": true,
"request": {
},
"response": {
"charger_types": [],
"connection_types": [],
"current_types": [],
"countries": [],
"data_providers": [],
"operators": [],
"status_types": [],
"submission_status_types": [],
"usage_types": [],
"usage_types": [],
"user_comment_types": [],
"checkin_status_types": []
}
}
]
}

2
evchargers/generate.go Normal file
View File

@@ -0,0 +1,2 @@
package main
//go:generate make proto

100
evchargers/handler/data.go Normal file
View File

@@ -0,0 +1,100 @@
package handler
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"github.com/micro/micro/v3/service/logger"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
func (e *Evchargers) loadPOIData(r io.Reader) (int, error) {
logger.Infof("Loading reference data")
dec := json.NewDecoder(r)
t, err := dec.Token()
if err != nil {
return 0, err
}
d, ok := t.(json.Delim)
if !ok || d.String() != "[" {
return 0, fmt.Errorf("unexpected token %v %+v", ok, t)
}
ctx := context.Background()
count := 0
for dec.More() {
// process each item in json array and insert into mongodb
var p Poi
if err := dec.Decode(&p); err != nil {
return 0, fmt.Errorf("error unmarshalling charger %s", err)
}
if len(p.SpatialPosition.Type) == 0 {
// blank so reconstruct
p.SpatialPosition.Type = "Point"
// long, lat not lat, long
p.SpatialPosition.Coordinates = []float64{p.Address.Longitude, p.Address.Latitude}
}
t := true
_, err := e.mdb.Database("ocm").Collection("poi").ReplaceOne(ctx, bson.D{bson.E{"ID", p.ID}}, p, &options.ReplaceOptions{Upsert: &t})
if err != nil {
return 0, err
}
count++
}
return count, nil
}
func (e *Evchargers) loadRefData(r io.Reader) error {
dec := json.NewDecoder(r)
var rd ReferenceData
if err := dec.Decode(&rd); err != nil {
return err
}
ctx := context.Background()
t := true
_, err := e.mdb.Database("ocm").Collection("reference").ReplaceOne(ctx, bson.D{bson.E{"_id", 1}}, rd, &options.ReplaceOptions{Upsert: &t})
if err != nil {
return err
}
return nil
}
func (e *Evchargers) refreshDataFromSource() {
start := time.Now()
logger.Infof("Refreshing data")
logger.Infof("Retrieving poi data")
rsp, err := http.Get(fmt.Sprintf("https://api.openchargemap.io/v3/poi/?output=json&key=%s&maxresults=10000000", e.conf.OCMKey))
if err != nil {
logger.Errorf("Error refreshing data %s", err)
return
}
defer rsp.Body.Close()
c, err := e.loadPOIData(rsp.Body)
if err != nil {
logger.Errorf("Error loading data %s", err)
return
}
logger.Infof("Updated %v items of POI data. Took %s", c, time.Since(start))
start = time.Now()
logger.Infof("Retrieving ref data")
rsp2, err := http.Get(fmt.Sprintf("https://api.openchargemap.io/v3/referencedata/?output=json&key=%s", e.conf.OCMKey))
if err != nil {
logger.Errorf("Error refreshing reference data %s", err)
return
}
defer rsp2.Body.Close()
if err := e.loadRefData(rsp2.Body); err != nil {
logger.Errorf("Error loading reference data %s", err)
return
}
logger.Infof("Updated ref data. Took %s", time.Since(start))
}

View File

@@ -0,0 +1,55 @@
package handler
import (
"context"
"io"
"os"
"testing"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func TestDataLoad(t *testing.T) {
t.SkipNow()
s := Evchargers{}
opts := []*options.ClientOptions{options.Client().ApplyURI("mongodb://127.0.0.1:27017/ocm")}
var err error
s.mdb, err = mongo.Connect(context.Background(), opts...)
if err != nil {
t.Fatalf("Error connecting to mongo %s", err)
}
var r io.Reader
r, err = os.Open("test.json")
if err != nil {
t.Fatalf("Error opening test data %s", err)
}
c, err := s.loadPOIData(r)
if err != nil {
t.Fatalf("Err loading data %s", err)
}
if c != 2 {
t.Errorf("Incorrect number of records %d", c)
}
}
func TestRefDataLoad(t *testing.T) {
t.SkipNow()
s := Evchargers{}
opts := []*options.ClientOptions{options.Client().ApplyURI("mongodb://127.0.0.1:27017/ocm")}
var err error
s.mdb, err = mongo.Connect(context.Background(), opts...)
if err != nil {
t.Fatalf("Error connecting to mongo %s", err)
}
var r io.Reader
r, err = os.Open("test-reference.json")
if err != nil {
t.Fatalf("Error opening test data %s", err)
}
err = s.loadRefData(r)
if err != nil {
t.Fatalf("Err loading data %s", err)
}
}

View File

@@ -0,0 +1,438 @@
package handler
import (
"context"
"io/ioutil"
"os"
"strconv"
"time"
"github.com/micro/micro/v3/service/errors"
"github.com/robfig/cron/v3"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/micro/micro/v3/service/config"
log "github.com/micro/micro/v3/service/logger"
evchargers "github.com/micro/services/evchargers/proto"
)
const (
defaultDistance = int64(5000) // 5km
)
var (
sphereIndexVersion = int32(3)
)
type Evchargers struct {
conf conf
mdb *mongo.Client
}
type conf struct {
MongoHost string `json:"mongo_host"`
CaCrt string `json:"ca_crt"`
OCMKey string `json:"ocm_key"`
}
func New() *Evchargers {
val, err := config.Get("micro.evchargers")
if err != nil {
log.Fatalf("Failed to load config")
}
var conf conf
if err := val.Scan(&conf); err != nil {
log.Fatalf("Failed to load config")
}
if len(conf.MongoHost) == 0 {
log.Fatalf("Missing mongodb host")
}
if len(conf.CaCrt) > 0 {
// write the cert to file
if err := ioutil.WriteFile(os.TempDir()+"/mongo.crt", []byte(conf.CaCrt), 0644); err != nil {
log.Fatalf("Failed to write crt file for mongodb %s", err)
}
}
opts := []*options.ClientOptions{options.Client().ApplyURI(conf.MongoHost)}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client, err := mongo.Connect(ctx, opts...)
if err != nil {
log.Fatalf("Failed to connect to mongo db %s", err)
}
ctx2, cancel2 := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel2()
if err := client.Ping(ctx2, nil); err != nil {
log.Fatalf("Failed to ping mongo db %s", err)
}
// make sure the indexes are set up
_, err = client.Database("ocm").Collection("poi").Indexes().CreateMany(context.Background(), []mongo.IndexModel{
{ // bounding box queries
Keys: bson.D{{"SpatialPosition.coordinates", "2dsphere"}},
Options: &options.IndexOptions{
SphereVersion: &sphereIndexVersion,
},
},
{ // distance queries
Keys: bson.D{{"SpatialPosition", "2dsphere"}},
Options: &options.IndexOptions{
SphereVersion: &sphereIndexVersion,
},
},
{
Keys: bson.M{"DateCreated": -1},
},
{
Keys: bson.M{"DateLastStatusUpdate": -1},
},
{
Keys: bson.M{"ID": -1},
},
})
if err != nil {
log.Fatalf("Failed to craete indexes %s", err)
}
ev := &Evchargers{conf: conf, mdb: client}
if len(conf.OCMKey) > 0 {
c := cron.New()
// 4am every Sunday for refresh
c.AddFunc("0 4 * * 0", ev.refreshDataFromSource)
c.Start()
}
return ev
}
func (e *Evchargers) Search(ctx context.Context, request *evchargers.SearchRequest, response *evchargers.SearchResponse) error {
toInt := func(in []string) []interface{} {
res := make([]interface{}, len(in))
for i, v := range in {
res[i], _ = strconv.Atoi(v)
}
return res
}
filters := bson.D{}
if len(request.ConnectionTypes) > 0 {
vals := bson.A{}
vals = append(vals, toInt(request.ConnectionTypes)...)
filters = append(filters, bson.E{"Connections.ConnectionTypeID", bson.D{{"$in", vals}}})
}
if request.Location != nil {
distance := defaultDistance
if request.Distance > 0 {
distance = request.Distance
}
filters = append(filters, bson.E{"SpatialPosition", bson.M{"$nearSphere": bson.M{"$geometry": bson.M{
"type": "Point",
"coordinates": []float64{float64(request.Location.Longitude), float64(request.Location.Latitude)},
},
"$maxDistance": distance,
},
}})
} else if request.Box != nil && request.Box.BottomLeft != nil {
filters = append(filters, bson.E{"SpatialPosition.coordinates", bson.M{"$geoWithin": bson.M{"$box": bson.A{
[]float32{request.Box.BottomLeft.Longitude, request.Box.BottomLeft.Latitude},
[]float32{request.Box.TopRight.Longitude, request.Box.TopRight.Latitude},
}}}})
}
if len(request.CountryId) > 0 {
i, _ := strconv.Atoi(request.CountryId)
filters = append(filters, bson.E{"AddressInfo.CountryID", i})
}
if len(request.Levels) > 0 {
vals := bson.A{}
vals = append(vals, toInt(request.Levels)...)
filters = append(filters, bson.E{"Connections.LevelID", bson.D{{"$in", vals}}})
}
if request.MinPower > 0 {
filters = append(filters, bson.E{"Connections.PowerKW", bson.D{{"$gte", request.MinPower}}})
}
if len(request.Operators) > 0 {
vals := bson.A{}
vals = append(vals, toInt(request.Operators)...)
filters = append(filters, bson.E{"OperatorID", bson.D{{"$in", vals}}})
}
if len(request.UsageTypes) > 0 {
vals := bson.A{}
vals = append(vals, toInt(request.UsageTypes)...)
filters = append(filters, bson.E{"UsageTypeID", bson.D{{"$in", vals}}})
}
maxLim := int64(100)
max := options.FindOptions{
Limit: &maxLim,
}
if request.MaxResults > 0 {
max.Limit = &request.MaxResults
}
crs, err := e.mdb.Database("ocm").Collection("poi").Find(ctx, filters, &max)
if err != nil {
log.Errorf("Error querying %s", err)
return errors.InternalServerError("evchargers.search", "Failed to query ev chargers")
}
defer crs.Close(ctx)
for crs.Next(ctx) {
var result Poi
if err := crs.Decode(&result); err != nil {
log.Errorf("Error decoding result %s", err)
return errors.InternalServerError("evchargers.search", "Failed to query ev chargers")
}
poi := &evchargers.Poi{
Id: strconv.Itoa(int(result.ID)),
DataProviderId: strconv.Itoa(int(result.DataProviderID)),
OperatorId: strconv.Itoa(int(result.OperatorID)),
UsageTypeId: strconv.Itoa(int(result.UsageTypeID)),
Address: &evchargers.Address{
Location: &evchargers.Coordinates{
Latitude: float32(result.Address.Latitude),
Longitude: float32(result.Address.Longitude),
},
Title: result.Address.Title,
AddressLine_1: result.Address.AddressLine1,
AddressLine_2: result.Address.AddressLine2,
Town: result.Address.Town,
StateOrProvince: result.Address.StateOrProvince,
AccessComments: result.Address.AccessComments,
Postcode: result.Address.Postcode,
CountryId: strconv.Itoa(int(result.Address.CountryID)),
},
Connections: marshalConnections(result.Connections),
NumPoints: int64(result.NumberOfPoints),
Cost: result.Cost,
}
if true { // verbose
poi.Operator = marshalOperator(result.OperatorInfo)
poi.UsageType = marshalUsageType(result.UsageType)
poi.Address.Country = marshalCountry(result.Address.Country)
}
response.Pois = append(response.Pois, poi)
}
return nil
}
func marshalCountry(in Country) *evchargers.Country {
return &evchargers.Country{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsoCode: in.ISOCode,
ContinentCode: in.ContinentCode,
}
}
func marshalConnections(in []Connection) []*evchargers.Connection {
res := make([]*evchargers.Connection, len(in))
for i, v := range in {
res[i] = &evchargers.Connection{
ConnectionTypeId: strconv.Itoa(int(v.TypeID)),
ConnectionType: &evchargers.ConnectionType{
Id: strconv.Itoa(int(v.Type.ID)),
Title: v.Type.Title,
FormalName: v.Type.FormalName,
IsDiscontinued: v.Type.IsDiscontinued,
IsObsolete: v.Type.IsObsolete,
},
Reference: v.Reference,
Level: strconv.Itoa(int(v.LevelID)),
Amps: float32(v.Amps),
Voltage: float32(v.Voltage),
Power: float32(v.Power),
Current: strconv.Itoa(int(v.CurrentTypeID)),
}
}
return res
}
func marshalDataProvider(in DataProvider) *evchargers.DataProvider {
return &evchargers.DataProvider{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
Website: in.WebsiteURL,
Comments: in.Comments,
DataProviderStatusType: marshalDataProviderStatus(in.DataProviderStatus),
License: in.License,
}
}
func marshalDataProviderStatus(in DataProviderStatus) *evchargers.DataProviderStatusType {
return &evchargers.DataProviderStatusType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsProviderEnabled: in.IsProviderEnabled,
}
}
func marshalOperator(in Operator) *evchargers.Operator {
return &evchargers.Operator{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
Website: in.WebsiteURL,
Comments: in.Comments,
IsPrivateIndividual: in.IsPrivateIndividual,
ContactEmail: in.ContactEmail,
PhonePrimary: in.PhonePrimary,
PhoneSecondary: in.PhoneSecondary,
FaultReportEmail: in.FaultReportEmail,
}
}
func marshalUsageType(in UsageType) *evchargers.UsageType {
return &evchargers.UsageType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsPayAtLocation: in.IsPayAtLocation,
IsMembershipRequired: in.IsMembershipRequired,
IsAccessKeyRequired: in.IsAccessKeyRequired,
}
}
func marshalCheckinStatusType(in CheckinStatusType) *evchargers.CheckinStatusType {
return &evchargers.CheckinStatusType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsPositive: in.IsPositive,
IsAutomated: in.IsAutomatedCheckin,
}
}
func marshalUserCommentType(in UserCommentType) *evchargers.UserCommentType {
return &evchargers.UserCommentType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
}
}
func marshalStatusType(in StatusType) *evchargers.StatusType {
return &evchargers.StatusType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsOperational: in.IsOperational,
}
}
func marshalCurrentType(in CurrentType) *evchargers.CurrentType {
return &evchargers.CurrentType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
Description: in.Description,
}
}
func marshalConnectionType(in ConnectionType) *evchargers.ConnectionType {
return &evchargers.ConnectionType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
FormalName: in.FormalName,
IsDiscontinued: in.IsDiscontinued,
IsObsolete: in.IsObsolete,
}
}
func marshalChargerType(in ChargerType) *evchargers.ChargerType {
return &evchargers.ChargerType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
Comments: in.Comments,
IsFastChargeCapable: in.IsFastChargeCapable,
}
}
func marshalSubmissionStatusType(in SubmissionStatusType) *evchargers.SubmissionStatusType {
return &evchargers.SubmissionStatusType{
Id: strconv.Itoa(int(in.ID)),
Title: in.Title,
IsLive: in.IsLive,
}
}
func (e *Evchargers) ReferenceData(ctx context.Context, request *evchargers.ReferenceDataRequest, response *evchargers.ReferenceDataResponse) error {
res := e.mdb.Database("ocm").Collection("reference").FindOne(ctx, bson.D{})
if res.Err() != nil {
log.Errorf("Error retrieving ref data %s", res.Err())
return errors.InternalServerError("evchargers.referencedata", "Error retrieving reference data")
}
var r ReferenceData
if err := res.Decode(&r); err != nil {
log.Errorf("Error decoding ref data %s", err)
return errors.InternalServerError("evchargers.referencedata", "Error retrieving reference data")
}
dps := make([]*evchargers.DataProvider, len(r.DataProviders))
for i, dp := range r.DataProviders {
dps[i] = marshalDataProvider(dp)
}
response.DataProviders = dps
cs := make([]*evchargers.Country, len(r.Countries))
for i, c := range r.Countries {
cs[i] = marshalCountry(c)
}
response.Countries = cs
cst := make([]*evchargers.CheckinStatusType, len(r.CheckinStatusTypes))
for i, v := range r.CheckinStatusTypes {
cst[i] = marshalCheckinStatusType(v)
}
response.CheckinStatusTypes = cst
uct := make([]*evchargers.UserCommentType, len(r.UserCommentTypes))
for i, v := range r.UserCommentTypes {
uct[i] = marshalUserCommentType(v)
}
response.UserCommentTypes = uct
st := make([]*evchargers.StatusType, len(r.StatusTypes))
for i, v := range r.StatusTypes {
st[i] = marshalStatusType(v)
}
response.StatusTypes = st
ut := make([]*evchargers.UsageType, len(r.UsageTypes))
for i, v := range r.UsageTypes {
ut[i] = marshalUsageType(v)
}
response.UsageTypes = ut
ct := make([]*evchargers.CurrentType, len(r.CurrentTypes))
for i, v := range r.CurrentTypes {
ct[i] = marshalCurrentType(v)
}
response.CurrentTypes = ct
connt := make([]*evchargers.ConnectionType, len(r.ConnectionTypes))
for i, v := range r.ConnectionTypes {
connt[i] = marshalConnectionType(v)
}
response.ConnectionTypes = connt
chrgt := make([]*evchargers.ChargerType, len(r.ChargerTypes))
for i, v := range r.ChargerTypes {
chrgt[i] = marshalChargerType(v)
}
response.ChargerTypes = chrgt
ops := make([]*evchargers.Operator, len(r.Operators))
for i, v := range r.Operators {
ops[i] = marshalOperator(v)
}
response.Operators = ops
sst := make([]*evchargers.SubmissionStatusType, len(r.SubmissionStatusTypes))
for i, v := range r.SubmissionStatusTypes {
sst[i] = marshalSubmissionStatusType(v)
}
response.SubmissionStatusTypes = sst
return nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,389 @@
[
{
"DataProvider": {
"WebsiteURL": "http://openchargemap.org",
"Comments": null,
"DataProviderStatusType": {
"IsProviderEnabled": true,
"ID": 1,
"Title": "Manual Data Entry"
},
"IsRestrictedEdit": false,
"IsOpenDataLicensed": true,
"IsApprovedImport": true,
"License": "Licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)",
"DateLastImported": null,
"ID": 1,
"Title": "Open Charge Map Contributors"
},
"OperatorInfo": {
"WebsiteURL": "https://evcharge.online/",
"Comments": "Rolec. Pre-payment, minimum topup £5.00",
"PhonePrimaryContact": null,
"PhoneSecondaryContact": null,
"IsPrivateIndividual": false,
"AddressInfo": null,
"BookingURL": null,
"ContactEmail": "Contact@power-portal.co.uk",
"FaultReportEmail": "Contact@power-portal.co.uk",
"IsRestrictedEdit": false,
"ID": 3295,
"Title": "evcharge.online"
},
"UsageType": {
"IsPayAtLocation": false,
"IsMembershipRequired": false,
"IsAccessKeyRequired": false,
"ID": 6,
"Title": "Private - For Staff, Visitors or Customers"
},
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"SubmissionStatus": {
"IsLive": true,
"ID": 200,
"Title": "Submission Published"
},
"UserComments": null,
"PercentageSimilarity": null,
"MediaItems": null,
"IsRecentlyVerified": true,
"DateLastVerified": "2021-09-27T16:41:00Z",
"ID": 189420,
"UUID": "E8B7D92B-8E36-43AB-909C-48A87A4BA279",
"ParentChargePointID": null,
"DataProviderID": 1,
"DataProvidersReference": null,
"OperatorID": 3295,
"OperatorsReference": null,
"UsageTypeID": 6,
"UsageCost": "Free",
"AddressInfo": {
"ID": 189779,
"Title": "Agrovista Allscott ",
"AddressLine1": "Agrovista Amenity",
"AddressLine2": "Walcot",
"Town": "Wrockwardine",
"StateOrProvince": "Telford and Wrekin",
"Postcode": "TF6 5DY",
"CountryID": 1,
"Country": {
"ISOCode": "GB",
"ContinentCode": "EU",
"ID": 1,
"Title": "United Kingdom"
},
"Latitude": 52.70780288629825,
"Longitude": -2.5934020726602967,
"ContactTelephone1": null,
"ContactTelephone2": null,
"ContactEmail": null,
"AccessComments": null,
"RelatedURL": null,
"Distance": null,
"DistanceUnit": 0
},
"Connections": [
{
"ID": 306355,
"ConnectionTypeID": 25,
"ConnectionType": {
"FormalName": "IEC 62196-2 Type 2",
"IsDiscontinued": false,
"IsObsolete": false,
"ID": 25,
"Title": "Type 2 (Socket Only)"
},
"Reference": "UKEV6095",
"StatusTypeID": 50,
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"LevelID": 2,
"Level": {
"Comments": "Over 2 kW, usually non-domestic socket type",
"IsFastChargeCapable": false,
"ID": 2,
"Title": "Level 2 : Medium (Over 2kW)"
},
"Amps": 32,
"Voltage": 400,
"PowerKW": 22.0,
"CurrentTypeID": 20,
"CurrentType": {
"Description": "Alternating Current - Three Phase",
"ID": 20,
"Title": "AC (Three-Phase)"
},
"Quantity": 1,
"Comments": null
},
{
"ID": 306356,
"ConnectionTypeID": 25,
"ConnectionType": {
"FormalName": "IEC 62196-2 Type 2",
"IsDiscontinued": false,
"IsObsolete": false,
"ID": 25,
"Title": "Type 2 (Socket Only)"
},
"Reference": "UKEV6096",
"StatusTypeID": 50,
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"LevelID": 2,
"Level": {
"Comments": "Over 2 kW, usually non-domestic socket type",
"IsFastChargeCapable": false,
"ID": 2,
"Title": "Level 2 : Medium (Over 2kW)"
},
"Amps": 32,
"Voltage": 400,
"PowerKW": 22.0,
"CurrentTypeID": 20,
"CurrentType": {
"Description": "Alternating Current - Three Phase",
"ID": 20,
"Title": "AC (Three-Phase)"
},
"Quantity": 1,
"Comments": null
},
{
"ID": 306357,
"ConnectionTypeID": 25,
"ConnectionType": {
"FormalName": "IEC 62196-2 Type 2",
"IsDiscontinued": false,
"IsObsolete": false,
"ID": 25,
"Title": "Type 2 (Socket Only)"
},
"Reference": "UKEV6097",
"StatusTypeID": 50,
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"LevelID": 2,
"Level": {
"Comments": "Over 2 kW, usually non-domestic socket type",
"IsFastChargeCapable": false,
"ID": 2,
"Title": "Level 2 : Medium (Over 2kW)"
},
"Amps": 32,
"Voltage": 400,
"PowerKW": 22.0,
"CurrentTypeID": 20,
"CurrentType": {
"Description": "Alternating Current - Three Phase",
"ID": 20,
"Title": "AC (Three-Phase)"
},
"Quantity": 1,
"Comments": null
},
{
"ID": 306358,
"ConnectionTypeID": 25,
"ConnectionType": {
"FormalName": "IEC 62196-2 Type 2",
"IsDiscontinued": false,
"IsObsolete": false,
"ID": 25,
"Title": "Type 2 (Socket Only)"
},
"Reference": "UKEV6098",
"StatusTypeID": 50,
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"LevelID": 2,
"Level": {
"Comments": "Over 2 kW, usually non-domestic socket type",
"IsFastChargeCapable": false,
"ID": 2,
"Title": "Level 2 : Medium (Over 2kW)"
},
"Amps": 32,
"Voltage": 400,
"PowerKW": 22.0,
"CurrentTypeID": 20,
"CurrentType": {
"Description": "Alternating Current - Three Phase",
"ID": 20,
"Title": "AC (Three-Phase)"
},
"Quantity": 1,
"Comments": null
}
],
"NumberOfPoints": 4,
"GeneralComments": null,
"DatePlanned": null,
"DateLastConfirmed": null,
"StatusTypeID": 50,
"DateLastStatusUpdate": "2021-09-27T16:41:00Z",
"MetadataValues": null,
"DataQualityLevel": 1,
"DateCreated": "2021-09-27T16:41:00Z",
"SubmissionStatusTypeID": 200
},
{
"DataProvider": {
"WebsiteURL": "http://openchargemap.org",
"Comments": null,
"DataProviderStatusType": {
"IsProviderEnabled": true,
"ID": 1,
"Title": "Manual Data Entry"
},
"IsRestrictedEdit": false,
"IsOpenDataLicensed": true,
"IsApprovedImport": true,
"License": "Licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)",
"DateLastImported": null,
"ID": 1,
"Title": "Open Charge Map Contributors"
},
"OperatorInfo": {
"WebsiteURL": null,
"Comments": null,
"PhonePrimaryContact": null,
"PhoneSecondaryContact": null,
"IsPrivateIndividual": null,
"AddressInfo": null,
"BookingURL": null,
"ContactEmail": null,
"FaultReportEmail": null,
"IsRestrictedEdit": null,
"ID": 1,
"Title": "(Unknown Operator)"
},
"UsageType": {
"IsPayAtLocation": null,
"IsMembershipRequired": null,
"IsAccessKeyRequired": null,
"ID": 1,
"Title": "Public"
},
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"SubmissionStatus": {
"IsLive": true,
"ID": 200,
"Title": "Submission Published"
},
"UserComments": null,
"PercentageSimilarity": null,
"MediaItems": null,
"IsRecentlyVerified": true,
"DateLastVerified": "2021-09-27T15:13:00Z",
"ID": 189419,
"UUID": "2A489FF2-CF7B-41D8-979D-08A9A251568C",
"ParentChargePointID": null,
"DataProviderID": 1,
"DataProvidersReference": null,
"OperatorID": 1,
"OperatorsReference": null,
"UsageTypeID": 1,
"UsageCost": "FREE",
"AddressInfo": {
"ID": 189778,
"Title": "CRMU EV Station",
"AddressLine1": "Main Street",
"AddressLine2": null,
"Town": "Coon Rapids",
"StateOrProvince": "Iowa",
"Postcode": "50059",
"CountryID": 2,
"Country": {
"ISOCode": "US",
"ContinentCode": "NA",
"ID": 2,
"Title": "United States"
},
"Latitude": 41.870824375016525,
"Longitude": -94.67421942978399,
"ContactTelephone1": null,
"ContactTelephone2": null,
"ContactEmail": null,
"AccessComments": null,
"RelatedURL": null,
"Distance": null,
"DistanceUnit": 0
},
"Connections": [
{
"ID": 306345,
"ConnectionTypeID": 1,
"ConnectionType": {
"FormalName": "SAE J1772-2009",
"IsDiscontinued": null,
"IsObsolete": null,
"ID": 1,
"Title": "Type 1 (J1772)"
},
"Reference": null,
"StatusTypeID": 50,
"StatusType": {
"IsOperational": true,
"IsUserSelectable": true,
"ID": 50,
"Title": "Operational"
},
"LevelID": 2,
"Level": {
"Comments": "Over 2 kW, usually non-domestic socket type",
"IsFastChargeCapable": false,
"ID": 2,
"Title": "Level 2 : Medium (Over 2kW)"
},
"Amps": 30,
"Voltage": 239,
"PowerKW": 6.6,
"CurrentTypeID": 10,
"CurrentType": {
"Description": "Alternating Current - Single Phase",
"ID": 10,
"Title": "AC (Single-Phase)"
},
"Quantity": 2,
"Comments": null
}
],
"NumberOfPoints": 2,
"GeneralComments": null,
"DatePlanned": null,
"DateLastConfirmed": null,
"StatusTypeID": 50,
"DateLastStatusUpdate": "2021-09-27T16:06:00Z",
"MetadataValues": null,
"DataQualityLevel": 1,
"DateCreated": "2021-09-27T15:13:00Z",
"SubmissionStatusTypeID": 200
}
]

157
evchargers/handler/types.go Normal file
View File

@@ -0,0 +1,157 @@
package handler
type Poi struct {
ID int32 `bson:"ID" json:"ID"`
DataProviderID int32 `bson:"DataProviderID" json:"DataProviderID"`
DataProvider DataProvider `bson:"DataProvider" json:"DataProvider"`
OperatorID int32 `bson:"OperatorID" json:"OperatorID"`
OperatorInfo Operator `bson:"OperatorInfo" json:"OperatorInfo"`
UsageTypeID int32 `bson:"UsageTypeID" json:"UsageTypeID"`
UsageType UsageType `bson:"UsageType" json:"UsageType"`
Cost string `bson:"UsageCost" json:"UsageCost"`
Address Address `bson:"AddressInfo" json:"AddressInfo"`
Connections []Connection `bson:"Connections" json:"Connections"`
NumberOfPoints int32 `bson:"NumberOfPoints" json:"NumberOfPoints"`
GeneralComments string `bson:"GeneralComments" json:"GeneralComments"`
StatusTypeID int32 `bson:"StatusTypeID" json:"StatusTypeID"`
StatusType StatusType `bson:"StatusType" json:"StatusType"`
SpatialPosition Position `bson:"SpatialPosition" json:"SpatialPosition"`
}
type Position struct {
Type string `bson:"type" json:"type"`
Coordinates []float64 `bson:"coordinates" json:"coordinates"`
}
type Address struct {
Title string `bson:"Title" json:"Title"`
Latitude float64 `bson:"Latitude" json:"Latitude"`
Longitude float64 `bson:"Longitude" json:"Longitude"`
AddressLine1 string `bson:"AddressLine1" json:"AddressLine1"`
AddressLine2 string `bson:"AddressLine2" json:"AddressLine2"`
Town string `bson:"Town" json:"Town"`
StateOrProvince string `bson:"StateOrProvince" json:"StateOrProvince"`
AccessComments string `bson:"AccessComments" json:"AccessComments"`
Postcode string `bson:"Postcode" json:"Postcode"`
CountryID int32 `bson:"CountryID" json:"CountryID"`
Country Country `bson:"Country" json:"Country"`
}
type Country struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
ISOCode string `bson:"ISOCode" json:"ISOCode"`
ContinentCode string `bson:"ContinentCode" json:"ContinentCode"`
}
type Connection struct {
TypeID int32 `bson:"ConnectionTypeID" json:"ConnectionTypeID"`
Type ConnectionType `bson:"ConnectionType" json:"ConnectionType"`
StatusTypeID int32 `bson:"StatusTypeID" json:"StatusTypeID"`
StatusType StatusType `bson:"StatusType" json:"StatusType"`
LevelID int32 `bson:"LevelID" json:"LevelID"`
Level ChargerType `bson:"Level" json:"Level"`
Amps float64 `bson:"Amps" json:"Amps"`
Voltage float64 `bson:"Voltage" json:"Voltage"`
Power float64 `bson:"PowerKW" json:"PowerKW"`
CurrentTypeID int32 `bson:"CurrentTypeID" json:"CurrentTypeID"`
CurrentType CurrentType `bson:"CurrentType" json:"CurrentType"`
Quantity int32 `bson:"Quantity" json:"Quantity"`
Reference string `bson:"Reference" json:"Reference"`
}
type ChargerType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
Comments string `bson:"Comments" json:"Comments"`
IsFastChargeCapable bool `bson:"IsFastChargeCapable" json:"IsFastChargeCapable"`
}
type CurrentType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
Description string `bson:"Description" json:"Description"`
}
type ConnectionType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
FormalName string `bson:"FormalName" json:"FormalName"`
IsDiscontinued bool `bson:"IsDiscontinued" json:"IsDiscontinued"`
IsObsolete bool `bson:"IsObsolete" json:"IsObsolete"`
}
type DataProvider struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
WebsiteURL string `bson:"WebsiteURL" json:"WebsiteURL"`
Comments string `bson:"Comments" json:"Comments"`
DataProviderStatus DataProviderStatus `bson:"DataProviderStatusType" json:"DataProviderStatusType"`
IsOpenDataLicensed bool `bson:"IsOpenDataLicensed" json:"IsOpenDataLicensed"`
License string `bson:"License" json:"License"`
}
type DataProviderStatus struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
IsProviderEnabled bool `bson:"IsProviderEnabled" json:"IsProviderEnabled"`
}
type Operator struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
WebsiteURL string `bson:"WebsiteURL" json:"WebsiteURL"`
Comments string `bson:"Comments" json:"Comments"`
PhonePrimary string `bson:"PhonePrimaryContact" json:"PhonePrimaryContact"`
PhoneSecondary string `bson:"PhoneSecondaryContact" json:"PhoneSecondaryContact"`
IsPrivateIndividual bool `bson:"IsPrivateIndividual" json:"IsPrivateIndividual"`
ContactEmail string `bson:"ContactEmail" json:"ContactEmail"`
FaultReportEmail string `bson:"FaultReportEmail" json:"FaultReportEmail"`
}
type UsageType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
IsPayAtLocation bool `bson:"IsPayAtLocation" json:"IsPayAtLocation"`
IsMembershipRequired bool `bson:"IsMembershipRequired" json:"IsMembershipRequired"`
IsAccessKeyRequired bool `bson:"IsAccessKeyRequired" json:"IsAccessKeyRequired"`
}
type StatusType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
IsUsageSelectable bool `bson:"IsUsageSelectable" json:"IsUsageSelectable"`
IsOperational bool `bson:"IsOperational" json:"IsOperational"`
}
type UserCommentType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
}
type CheckinStatusType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
IsPositive bool `bson:"IsPositive" json:"IsPositive"`
IsAutomatedCheckin bool `bson:"IsAutomatedCheckin" json:"IsAutomatedCheckin"`
}
type ReferenceData struct {
ChargerTypes []ChargerType `bson:"ChargerTypes" json:"ChargerTypes"`
ConnectionTypes []ConnectionType `bson:"ConnectionTypes" json:"ConnectionTypes"`
CurrentTypes []CurrentType `bson:"CurrentTypes" json:"CurrentTypes"`
Countries []Country `bson:"Countries" json:"Countries"`
DataProviders []DataProvider `bson:"DataProviders" json:"DataProviders"`
Operators []Operator `bson:"Operators" json:"Operators"`
StatusTypes []StatusType `bson:"StatusTypes" json:"StatusTypes"`
UsageTypes []UsageType `bson:"UsageTypes" json:"UsageTypes"`
UserCommentTypes []UserCommentType `bson:"UserCommentTypes" json:"UserCommentTypes"`
CheckinStatusTypes []CheckinStatusType `bson:"CheckinStatusTypes" json:"CheckinStatusTypes"`
SubmissionStatusTypes []SubmissionStatusType `bson:"SubmissionStatusTypes" json:"SubmissionStatusTypes"`
}
type SubmissionStatusType struct {
ID int32 `bson:"ID" json:"ID"`
Title string `bson:"Title" json:"Title"`
IsLive bool `bson:"IsLive" json:"IsLive"`
}

25
evchargers/main.go Normal file
View File

@@ -0,0 +1,25 @@
package main
import (
"github.com/micro/services/evchargers/handler"
pb "github.com/micro/services/evchargers/proto"
"github.com/micro/micro/v3/service"
"github.com/micro/micro/v3/service/logger"
)
func main() {
// Create service
srv := service.New(
service.Name("evchargers"),
service.Version("latest"),
)
// Register handler
pb.RegisterEvchargersHandler(srv.Server(), handler.New())
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}

1
evchargers/micro.mu Normal file
View File

@@ -0,0 +1 @@
service evchargers

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: proto/evchargers.proto
package evchargers
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
import (
context "context"
api "github.com/micro/micro/v3/service/api"
client "github.com/micro/micro/v3/service/client"
server "github.com/micro/micro/v3/service/server"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Reference imports to suppress errors if they are not otherwise used.
var _ api.Endpoint
var _ context.Context
var _ client.Option
var _ server.Option
// Api Endpoints for Evchargers service
func NewEvchargersEndpoints() []*api.Endpoint {
return []*api.Endpoint{}
}
// Client API for Evchargers service
type EvchargersService interface {
Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error)
ReferenceData(ctx context.Context, in *ReferenceDataRequest, opts ...client.CallOption) (*ReferenceDataResponse, error)
}
type evchargersService struct {
c client.Client
name string
}
func NewEvchargersService(name string, c client.Client) EvchargersService {
return &evchargersService{
c: c,
name: name,
}
}
func (c *evchargersService) Search(ctx context.Context, in *SearchRequest, opts ...client.CallOption) (*SearchResponse, error) {
req := c.c.NewRequest(c.name, "Evchargers.Search", in)
out := new(SearchResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *evchargersService) ReferenceData(ctx context.Context, in *ReferenceDataRequest, opts ...client.CallOption) (*ReferenceDataResponse, error) {
req := c.c.NewRequest(c.name, "Evchargers.ReferenceData", in)
out := new(ReferenceDataResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Evchargers service
type EvchargersHandler interface {
Search(context.Context, *SearchRequest, *SearchResponse) error
ReferenceData(context.Context, *ReferenceDataRequest, *ReferenceDataResponse) error
}
func RegisterEvchargersHandler(s server.Server, hdlr EvchargersHandler, opts ...server.HandlerOption) error {
type evchargers interface {
Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error
ReferenceData(ctx context.Context, in *ReferenceDataRequest, out *ReferenceDataResponse) error
}
type Evchargers struct {
evchargers
}
h := &evchargersHandler{hdlr}
return s.Handle(s.NewHandler(&Evchargers{h}, opts...))
}
type evchargersHandler struct {
EvchargersHandler
}
func (h *evchargersHandler) Search(ctx context.Context, in *SearchRequest, out *SearchResponse) error {
return h.EvchargersHandler.Search(ctx, in, out)
}
func (h *evchargersHandler) ReferenceData(ctx context.Context, in *ReferenceDataRequest, out *ReferenceDataResponse) error {
return h.EvchargersHandler.ReferenceData(ctx, in, out)
}

View File

@@ -0,0 +1,228 @@
syntax = "proto3";
package evchargers;
option go_package = "./proto;evchargers";
service Evchargers {
rpc Search(SearchRequest) returns (SearchResponse) {}
rpc ReferenceData(ReferenceDataRequest) returns (ReferenceDataResponse) {}
}
// Search by giving a coordinate and a max distance, or bounding box and optional filters
message SearchRequest {
// Coordinates from which to begin search
Coordinates location = 1;
// Search distance from point in metres, defaults to 5000m
int64 distance = 2;
// Bounding box to search within (top left and bottom right coordinates)
BoundingBox box = 3;
// Maximum number of results to return, defaults to 100
int64 max_results = 4;
// Country ID
string country_id = 5;
// IDs of the the EV charger operator
repeated string operators = 6;
// IDs of the connection type
repeated string connection_types = 7;
// Supported charging levels
repeated string levels = 8;
// Minimum power in KW. Note: data not available for many chargers
int64 min_power = 9;
// Usage of the charge point (is it public, membership required, etc)
repeated string usage_types = 11;
// TODO https://openchargemap.org/site/develop/api#POI
// verbose / compact to only return IDs for ref data
// polygon
// polyline
}
message Coordinates {
float latitude = 1;
float longitude = 2;
}
// Box to search (top left and bottom right coordinates)
message BoundingBox {
Coordinates bottom_left = 1;
Coordinates top_right = 2;
}
message SearchResponse {
repeated Poi pois = 1;
}
message Poi {
// The ID of the charger
string id = 1;
// The ID of the data provider
string data_provider_id = 2;
// The ID of the operator of the charger
string operator_id = 3;
// The type of usage for this charger point (is it public, membership required, etc)
string usage_type_id = 4;
// The address
Address address = 5;
// The connections available at this charge point
repeated Connection connections = 6;
// The number of charging points
int64 num_points = 7;
// The cost of charging
string cost = 8;
// The operator
Operator operator = 10;
// The type of usage
UsageType usage_type = 11;
}
message Address {
Coordinates location = 1;
string title = 2;
string address_line_1 = 3;
string address_line_2 = 4;
string town = 5;
string state_or_province = 6;
// Any comments about how to access the charger
string access_comments = 7;
string postcode = 8;
string country_id = 9;
Country country = 10;
}
message Connection {
// The ID of the connection type
string connection_type_id = 1;
string reference = 2;
// The level of charging power available
string level = 4;
// The amps offered
float amps = 5;
// The voltage offered
float voltage = 6;
// The power in KW
float power = 7;
// The current
string current = 8;
ConnectionType connection_type = 9;
}
// Retrieve reference data as used by this API
message ReferenceDataRequest {}
message ReferenceDataResponse {
// The types of charger
repeated ChargerType charger_types = 1;
// The types of connection
repeated ConnectionType connection_types = 2;
// The types of current
repeated CurrentType current_types = 3;
// The countries
repeated Country countries = 4;
// The providers of the charger data
repeated DataProvider data_providers = 5;
// The companies operating the chargers
repeated Operator operators = 6;
// The status of the charger
repeated StatusType status_types = 7;
// The status of a submission
repeated SubmissionStatusType submission_status_types = 8;
// The different types of usage
repeated UsageType usage_types = 9;
// The types of user comment
repeated UserCommentType user_comment_types = 10;
// The types of checkin status
repeated CheckinStatusType checkin_status_types = 11;
}
message ChargerType {
string id = 1;
string title = 2;
string comments = 3;
// Is this 40KW+
bool is_fast_charge_capable = 4;
}
message ConnectionType {
string id = 1;
string title = 2;
string formal_name = 3;
bool is_discontinued = 4;
bool is_obsolete = 5;
}
message CurrentType {
string id = 1;
string title = 2;
string description = 3;
}
message Country {
string id = 1;
string title = 2;
string iso_code = 3;
string continent_code = 4;
}
message DataProvider {
string id = 1;
string title = 2;
string website = 3;
string comments = 4;
DataProviderStatusType data_provider_status_type = 5;
// How is this data licensed
string license = 6;
}
message DataProviderStatusType {
string id = 1;
string title = 2;
bool is_provider_enabled = 3;
}
message Operator {
string id = 1;
string title = 2;
string website = 3;
string comments = 4;
// Is this operator a private individual vs a company
bool is_private_individual = 5;
string contact_email = 6;
string phone_primary = 7;
string phone_secondary = 8;
string fault_report_email = 9;
}
message StatusType {
string id = 1;
string title = 2;
bool is_operational = 3;
}
message SubmissionStatusType {
string id = 1;
string title = 2;
bool is_live = 3;
}
message UsageType {
string id = 1;
string title = 2;
bool is_pay_at_location = 3;
bool is_membership_required = 4;
bool is_access_key_required = 5;
}
message UserCommentType {
string id = 1;
string title = 2;
}
message CheckinStatusType {
string id = 1;
string title = 2;
bool is_positive = 3;
bool is_automated = 4;
}

View File

@@ -0,0 +1,5 @@
{
"name": "evchargers",
"icon": "🔋",
"category": "travel"
}

View File

@@ -0,0 +1,4 @@
curl "https://api.m3o.com/v1/evchargers/ReferenceData" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MICRO_API_TOKEN" \
-d '{}'

View File

@@ -0,0 +1,14 @@
package example
import (
"fmt"
"github.com/micro/services/clients/go/evchargers"
"os"
)
// Retrieve reference data as used by this API
func GetReferenceData() {
evchargersService := evchargers.NewEvchargersService(os.Getenv("MICRO_API_TOKEN"))
rsp, err := evchargersService.ReferenceData(&evchargers.ReferenceDataRequest{})
fmt.Println(rsp, err)
}

View File

@@ -0,0 +1,12 @@
import * as evchargers from "m3o/evchargers";
// Retrieve reference data as used by this API
async function GetReferenceData() {
let evchargersService = new evchargers.EvchargersService(
process.env.MICRO_API_TOKEN
);
let rsp = await evchargersService.referenceData({});
console.log(rsp);
}
await GetReferenceData();

View File

@@ -0,0 +1,16 @@
curl "https://api.m3o.com/v1/evchargers/Search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MICRO_API_TOKEN" \
-d '{
"box": {
"bottom_left": {
"latitude": 51.52627543859447,
"longitude": -0.03635349400295168
},
"top_right": {
"latitude": 51.56717121807993,
"longitude": -0.002293530559768285
}
},
"max_results": 2
}'

View File

@@ -0,0 +1,11 @@
curl "https://api.m3o.com/v1/evchargers/Search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MICRO_API_TOKEN" \
-d '{
"distance": 2000,
"location": {
"latitude": 51.53336351319885,
"longitude": -0.0252
},
"max_results": 2
}'

View File

@@ -0,0 +1,14 @@
curl "https://api.m3o.com/v1/evchargers/Search" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MICRO_API_TOKEN" \
-d '{
"distance": 2000,
"levels": [
"3"
],
"location": {
"latitude": 51.53336351319885,
"longitude": -0.0252
},
"max_results": 2
}'

View File

@@ -0,0 +1,16 @@
package example
import (
"fmt"
"github.com/micro/services/clients/go/evchargers"
"os"
)
// Search by giving a coordinate and a max distance, or bounding box and optional filters
func SearchByBoundingBox() {
evchargersService := evchargers.NewEvchargersService(os.Getenv("MICRO_API_TOKEN"))
rsp, err := evchargersService.Search(&evchargers.SearchRequest{
Box: &evchargers.BoundingBox{},
})
fmt.Println(rsp, err)
}

View File

@@ -0,0 +1,20 @@
package example
import (
"fmt"
"github.com/micro/services/clients/go/evchargers"
"os"
)
// Search by giving a coordinate and a max distance, or bounding box and optional filters
func SearchByLocation() {
evchargersService := evchargers.NewEvchargersService(os.Getenv("MICRO_API_TOKEN"))
rsp, err := evchargersService.Search(&evchargers.SearchRequest{
Distance: 2000,
Location: &evchargers.Coordinates{
Latitude: 51.53336351319885,
Longitude: -0.0252,
},
})
fmt.Println(rsp, err)
}

View File

@@ -0,0 +1,21 @@
package example
import (
"fmt"
"github.com/micro/services/clients/go/evchargers"
"os"
)
// Search by giving a coordinate and a max distance, or bounding box and optional filters
func SearchWithFiltersFastChargersOnly() {
evchargersService := evchargers.NewEvchargersService(os.Getenv("MICRO_API_TOKEN"))
rsp, err := evchargersService.Search(&evchargers.SearchRequest{
Distance: 2000,
Levels: []string{"3"},
Location: &evchargers.Coordinates{
Latitude: 51.53336351319885,
Longitude: -0.0252,
},
})
fmt.Println(rsp, err)
}

View File

@@ -0,0 +1,24 @@
import * as evchargers from "m3o/evchargers";
// Search by giving a coordinate and a max distance, or bounding box and optional filters
async function SearchByBoundingBox() {
let evchargersService = new evchargers.EvchargersService(
process.env.MICRO_API_TOKEN
);
let rsp = await evchargersService.search({
box: {
bottom_left: {
latitude: 51.52627543859447,
longitude: -0.03635349400295168,
},
top_right: {
latitude: 51.56717121807993,
longitude: -0.002293530559768285,
},
},
max_results: 2,
});
console.log(rsp);
}
await SearchByBoundingBox();

View File

@@ -0,0 +1,19 @@
import * as evchargers from "m3o/evchargers";
// Search by giving a coordinate and a max distance, or bounding box and optional filters
async function SearchByLocation() {
let evchargersService = new evchargers.EvchargersService(
process.env.MICRO_API_TOKEN
);
let rsp = await evchargersService.search({
distance: 2000,
location: {
latitude: 51.53336351319885,
longitude: -0.0252,
},
max_results: 2,
});
console.log(rsp);
}
await SearchByLocation();

View File

@@ -0,0 +1,20 @@
import * as evchargers from "m3o/evchargers";
// Search by giving a coordinate and a max distance, or bounding box and optional filters
async function SearchWithFiltersFastChargersOnly() {
let evchargersService = new evchargers.EvchargersService(
process.env.MICRO_API_TOKEN
);
let rsp = await evchargersService.search({
distance: 2000,
levels: ["3"],
location: {
latitude: 51.53336351319885,
longitude: -0.0252,
},
max_results: 2,
});
console.log(rsp);
}
await SearchWithFiltersFastChargersOnly();

2
go.mod
View File

@@ -39,6 +39,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/paulmach/go.geo v0.0.0-20180829195134-22b514266d33
github.com/pquerna/otp v1.3.0
github.com/robfig/cron/v3 v3.0.1
github.com/sendgrid/rest v2.6.4+incompatible // indirect
github.com/sendgrid/sendgrid-go v3.10.0+incompatible
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
@@ -48,6 +49,7 @@ require (
github.com/tkuchiki/go-timezone v0.2.2
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
github.com/ttacon/libphonenumber v1.2.1 // indirect
go.mongodb.org/mongo-driver v1.7.2
go.opencensus.io v0.22.4 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110

102
go.sum
View File

@@ -26,6 +26,7 @@ github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6L
github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/HdrHistogram/hdrhistogram-go v1.1.0 h1:6dpdDPTRoo78HxAJ6T1HfMiKSnqhgRRqzCuPshRkQ7I=
github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
@@ -56,6 +57,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
@@ -74,6 +76,7 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY=
github.com/cloudflare/cloudflare-go v0.10.9/go.mod h1:5TrsWH+3f4NV6WjtS5QFp+DifH81rph40gU374Sh0dQ=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@@ -90,6 +93,7 @@ github.com/crufter/nested v0.0.0-20210903145606-dea42c476b37/go.mod h1:YNI79Hwfl
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
@@ -121,6 +125,7 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getkin/kin-openapi v0.26.0 h1:xKIW5Z5wAfutxGBH+rr9qu0Ywfb/E1bPWkYLKRYfEuU=
github.com/getkin/kin-openapi v0.26.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
@@ -136,8 +141,35 @@ github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -146,10 +178,13 @@ github.com/gojuno/go.osrm v0.1.1-0.20200217151037-435fc3e1d3d4 h1:ZhyiVDRMAdbMPF
github.com/gojuno/go.osrm v0.1.1-0.20200217151037-435fc3e1d3d4/go.mod h1:XPCHB/Ir2/vHnqhKlfUxIiUGHFtTzgrRxD89JdkJhrs=
github.com/golang-jwt/jwt v0.0.0-20210529014511-0f726ea0e725 h1:fMKUGzqjXWLpddTodG8KO9moexa9bZMFQSkJRDefXpI=
github.com/golang-jwt/jwt v0.0.0-20210529014511-0f726ea0e725/go.mod h1:aHjnehRD4y8BHKf+z8wAPIRTd/3cm+FrvC6kQIDhV3o=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -169,6 +204,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1 h1:jAbXjIeW2ZSW2AwFxlGTDoc2CjI2XujLkV3ArsZFCvc=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -176,7 +213,9 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -223,6 +262,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
github.com/improbable-eng/grpc-web v0.13.0 h1:7XqtaBWaOCH0cVGKHyvhtcuo6fgW32Y10yRKrDHFHOc=
github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
@@ -238,6 +278,7 @@ github.com/jackc/pgconn v1.8.0 h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
@@ -281,6 +322,7 @@ github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -288,6 +330,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kevinburke/go-types v0.0.0-20201208005256-aee49f568a20 h1:Tux1t20gPWp4zkjCCdv2rLAwp+T3jCEROsEuvXp50FI=
github.com/kevinburke/go-types v0.0.0-20201208005256-aee49f568a20/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw=
github.com/kevinburke/go.uuid v1.2.0 h1:+1qP8NdkJfgOSTrrrUuA7h0djr1VY77HFXYjR+zUcUo=
@@ -297,6 +341,8 @@ github.com/kevinburke/rest v0.0.0-20210506044642-5611499aa33c/go.mod h1:pD+iEcdA
github.com/kevinburke/twilio-go v0.0.0-20210327194925-1623146bcf73 h1:PSsFm2SRpq9LnaRHLz4u9ZZ3liWjgXM6OMxXE4/qlgY=
github.com/kevinburke/twilio-go v0.0.0-20210327194925-1623146bcf73/go.mod h1:Fm9alkN1/LPVY1eqD/psyMwPWE4VWl4P01/nTYZKzBk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ=
@@ -304,10 +350,12 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA=
github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w=
@@ -315,6 +363,7 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA=
github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ=
@@ -323,6 +372,8 @@ github.com/m3o/goduckgo v0.0.0-20210630141545-c760fe67b945/go.mod h1:wQOw7PY6509
github.com/m3o/m3o-go v0.0.0-20210915113633-a4f018a78d79 h1:WJ7AfarocyVqqvXLzjA11U4PEmZ6P2fqMtAXNhtmNU4=
github.com/m3o/m3o-go/client v0.0.0-20210421144725-8bfd7992ada3 h1:RVt7rqWl4al36BH9OY9k7IXnnooOP0Feanu1bed6X2s=
github.com/m3o/m3o-go/client v0.0.0-20210421144725-8bfd7992ada3/go.mod h1:vmeaYrKYpgVNhny/l7iH8mXS88S7ijUiYni3gZUrCq0=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattheath/base62 v0.0.0-20150408093626-b80cdc656a7a h1:rnrxZue85aKdMU4nJ50GgKA31lCaVbft+7Xl8OXj55U=
github.com/mattheath/base62 v0.0.0-20150408093626-b80cdc656a7a/go.mod h1:hJJYoBMTZIONmUEpX3+9v2057zuRM0n3n77U4Ob4wE4=
github.com/mattheath/kala v0.0.0-20171219141654-d6276794bf0e h1:cj+w63ez19o7y7vunA8Q3rUIWwKEOUx7foqjnr4qbtI=
@@ -342,6 +393,7 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -358,9 +410,12 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nightlyone/lockfile v1.0.0 h1:RHep2cFKK4PonZJDdEl4GmkabuhbsRMgk/k3uAmxBiA=
github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatROs6LzC841CI=
@@ -368,6 +423,7 @@ github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/Hzq
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.3/go.mod h1:YZeBtGzYYEsCHp2LST/u/0NDwGkRoBtmn1cIWCJiS6M=
@@ -375,10 +431,12 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -397,6 +455,7 @@ github.com/paulmach/go.geo v0.0.0-20180829195134-22b514266d33 h1:doG/0aLlWE6E4nd
github.com/paulmach/go.geo v0.0.0-20180829195134-22b514266d33/go.mod h1:btFYk/ltlMU7ZKguHS7zQrwHYCtLoXGTaa44OsPbEVw=
github.com/paulmach/go.geojson v1.4.0 h1:5x5moCkCtDo5x8af62P9IOAYGQcYHtxz2QJ3x1DoCgY=
github.com/paulmach/go.geojson v1.4.0/go.mod h1:YaKx1hKpWF+T2oj2lFJPsW/t1Q5e1jQI61eoQSTwpIs=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -423,7 +482,12 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
@@ -439,12 +503,15 @@ github.com/sendgrid/rest v2.6.4+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV
github.com/sendgrid/sendgrid-go v3.10.0+incompatible h1:aSYyurHxEZSDy7kxhvZ4fH0inNkEEmRssZNbAmETR2c=
github.com/sendgrid/sendgrid-go v3.10.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
@@ -452,6 +519,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDq
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -467,6 +536,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
github.com/tkuchiki/go-timezone v0.2.2 h1:MdHR65KwgVTwWFQrota4SKzc4L5EfuH5SdZZGtk/P2Q=
github.com/tkuchiki/go-timezone v0.2.2/go.mod h1:oFweWxYl35C/s7HMVZXiA19Jr9Y0qJHMaG/J2TES4LY=
@@ -487,14 +558,24 @@ github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
github.com/xanzy/go-gitlab v0.35.1/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.mongodb.org/mongo-driver v1.7.2 h1:pFttQyIiJUHEn50YfZgC9ECjITMT44oiN36uArf/OFg=
go.mongodb.org/mongo-driver v1.7.2/go.mod h1:Q4oFMbo1+MSNqICAdYMlC/zSTrwCogR4R8NzkI+yfU8=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -519,6 +600,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -538,6 +620,7 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587 h1:5Uz0rkjCFu9BC9gCRN7EkwVvhNyQgGWb8KNJrPwBoHY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -550,12 +633,14 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -597,6 +682,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -614,9 +700,11 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -644,6 +732,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -658,10 +748,14 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -675,6 +769,7 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e h1:4nW4NLDYnU28ojHaHO8OVxFHk/aQ33U01a9cjED+pzE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -684,7 +779,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
@@ -732,6 +829,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
@@ -752,6 +850,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -759,10 +858,13 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclp
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/datatypes v1.0.1 h1:6npnXbBtjpSb7FFVA2dG/llyTN8tvZfbUqs+WyLrYgQ=
gorm.io/datatypes v1.0.1/go.mod h1:HEHoUU3/PO5ZXfAJcVWl11+zWlE16+O0X2DgJEb4Ixs=
gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
gorm.io/driver/postgres v1.0.8 h1:PAgM+PaHOSAeroTjHkCHCBIHHoBIf9RgPWGo8dF2DA8=
gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
gorm.io/driver/sqlserver v1.0.7 h1:uwUtb0kdFwW5PkRbd2KJ2h4wlsqvLSjox1XVg/RnzRE=
gorm.io/driver/sqlserver v1.0.7/go.mod h1:ng66aHI47ZIKz/vvnxzDoonzmTS8HXP+JYlgg67wOog=
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=