upstream sync

This commit is contained in:
Senad Uka
2023-10-12 05:23:34 +02:00
parent d3011d77ff
commit 7369739bdd
21 changed files with 1686 additions and 85 deletions

View File

@@ -23,9 +23,10 @@ func Load() error {
Service: Service{
// 9000 DEFAULT FOR DEV ENVIRONMENT
Port: getEnv("NOVATECH_SERVICE_PORT", "9000"),
Environment: getEnv("NOVATECH_SERVICE_ENVIRONMENT", "DEV"),
BlockchainSecret: getEnv("NOVATECH_SERVICE_BLOCKCHAIN_SECRET", "novatech_service_blockchain_secret"),
Port: getEnv("NOVATECH_SERVICE_PORT", "9000"),
Environment: getEnv("NOVATECH_SERVICE_ENVIRONMENT", "DEV"),
BlockchainSecret: getEnv("NOVATECH_SERVICE_BLOCKCHAIN_SECRET", "novatech_service_blockchain_secret"),
MapboxAccessToken: getEnv("NOVATECH_SERVICE_MAPBOX_ACCESS_TOKEN", ""),
},
AdminService: Service{
// 8080 DEFAULT FOR DEV ENVIRONMENT

View File

@@ -10,10 +10,11 @@ type Config struct {
// Service contains configuration for service
type Service struct {
Port string
Environment string
WebPageURL string
BlockchainSecret string
Port string
Environment string
WebPageURL string
BlockchainSecret string
MapboxAccessToken string
}
// Blockchain contains configuration for blockchain

View File

@@ -150,9 +150,6 @@ func GetContractStatuses(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": models.GetContractStatuses()})
}
func CreateContract(c *gin.Context) {
var payload models.CreateContractRequestPayload

View File

@@ -0,0 +1,32 @@
package controllers
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
"gitlab.com/pactual1/backend/config"
"gitlab.com/pactual1/backend/services/location"
)
func SearchPlace(c *gin.Context) {
query := c.Query("q")
if query == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "query is empty"})
return
}
mapboxAccessToken := config.AppConfig.Service.MapboxAccessToken
locationClient := location.NewService(mapboxAccessToken)
places, err := locationClient.SearchPlace(c, query)
if err != nil {
log.Printf("SearchPlace Error: Service error: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Service error"})
return
}
// Respond with the buyers and the total count
c.JSON(http.StatusOK, gin.H{"data": places})
}

View File

@@ -11,4 +11,5 @@ NOVATECH_SERVICE_BLOCKCHAIN_SECRET="abc&1*~#^2^#s0^=)^^7%b34"
NOVATECH_BLOCKCHAIN_NETWORK_ENDPOINT="https://polygon-mumbai.infura.io/v3/4458cf4d1689497b9a38b1d6bbf05e78"
NOVATECH_BLOCKCHAIN_CONTRACT_ADDRESS=0x121Cb4bFEeDb55d598D8F5e9EeDF8bB14c421d96
NOVATECH_BLOCKCHAIN_WALLET_ADDRESS=0x20eff5decaed29bd64f0c6385956363eeaaf4d3e
NOVATECH_BLOCKCHAIN_WALLET_PRIVATE_KEY=PRIVATE_KEY
NOVATECH_BLOCKCHAIN_WALLET_PRIVATE_KEY=PRIVATE_KEY
NOVATECH_SERVICE_MAPBOX_ACCESS_TOKEN=pk.ey

6
go.mod
View File

@@ -9,8 +9,11 @@ require (
github.com/jinzhu/gorm v1.9.16
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.8.0
github.com/mailru/easyjson v0.7.7
github.com/pkg/errors v0.9.1
github.com/qor/admin v1.2.0
github.com/stretchr/testify v1.8.4
github.com/valyala/fasthttp v1.40.0
)
replace gitlab.com/pactual1/backend => ./
@@ -19,6 +22,7 @@ require (
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/VictoriaMetrics/fastcache v1.12.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/bits-and-blooms/bitset v1.8.0 // indirect
@@ -62,6 +66,7 @@ require (
github.com/holiman/uint256 v1.2.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
@@ -98,6 +103,7 @@ require (
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
golang.org/x/arch v0.5.0 // indirect
golang.org/x/crypto v0.13.0 // indirect

26
go.sum
View File

@@ -7,6 +7,9 @@ github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBK
github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40=
github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
@@ -168,8 +171,11 @@ github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -191,6 +197,8 @@ github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNa
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
@@ -326,6 +334,11 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc=
github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yosssi/gohtml v0.0.0-20200519115854-476f5b4b8047 h1:YWaOkupKL+BRRJSWRq/uhSkWXc1K0QVIYVG36XUBGOc=
@@ -338,6 +351,7 @@ golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
@@ -351,6 +365,8 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
@@ -361,6 +377,11 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -369,11 +390,16 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -9,60 +9,60 @@ import (
type Contract struct {
gorm.Model
Name string `json:"name"`
DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"`
BuyerID uint `json:"buyerId"`
SellerID uint `json:"sellerId"`
Description string `json:"description"`
StartLat float64 `json:"startLat"`
StartLon float64 `json:"startLon"`
EndLat float64 `json:"endLat"`
EndLon float64 `json:"endLon"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Status string `json:"status"`
BlockchainID string `json:"blockchainId"`
ContractInfos []ContractInfo `json:"contractInfos"`
ProductID uint `json:"productId"`
MaxTemp float64 `json:"maxTemp"`
MinTemp float64 `json:"minTemp"`
ArrivalDate time.Time `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
BuyerName string `json:"buyerName" gorm:"-"`
NumberOfDevices int `json:"numberOfDevices" gorm:"-"`
Name string `json:"name"`
DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"`
BuyerID uint `json:"buyerId"`
SellerID uint `json:"sellerId"`
Description string `json:"description"`
StartPlaceName string `json:"startPlaceName"`
StartLat float64 `json:"startLat"`
StartLon float64 `json:"startLon"`
EndPlaceName string `json:"endPlaceName"`
EndLat float64 `json:"endLat"`
EndLon float64 `json:"endLon"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Status string `json:"status"`
BlockchainID string `json:"blockchainId"`
ContractInfos []ContractInfo `json:"contractInfos"`
ProductID uint `json:"productId"`
MaxTemp float64 `json:"maxTemp"`
MinTemp float64 `json:"minTemp"`
ArrivalDate time.Time `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
BuyerName string `json:"buyerName" gorm:"-"`
NumberOfDevices int `json:"numberOfDevices" gorm:"-"`
}
type ContractResponse struct {
BaseModel
Name string `json:"name"`
DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"`
BuyerID uint `json:"buyerId"`
SellerID uint `json:"sellerId"`
Description string `json:"description"`
StartLat float64 `json:"startLat"`
StartLon float64 `json:"startLon"`
EndLat float64 `json:"endLat"`
EndLon float64 `json:"endLon"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Status string `json:"status"`
BlockchainID string `json:"blockchainId"`
ContractInfos []ContractInfo `json:"contractInfos"`
ProductID uint `json:"productId"`
MaxTemp float64 `json:"maxTemp"`
MinTemp float64 `json:"minTemp"`
ArrivalDate time.Time `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
BuyerName string `json:"buyerName" gorm:"-"`
NumberOfDevices int `json:"numberOfDevices" gorm:"-"`
Name string `json:"name"`
DeviceIDs pq.Int64Array `json:"deviceIds" gorm:"type:integer[]"`
BuyerID uint `json:"buyerId"`
SellerID uint `json:"sellerId"`
Description string `json:"description"`
StartLat float64 `json:"startLat"`
StartLon float64 `json:"startLon"`
EndLat float64 `json:"endLat"`
EndLon float64 `json:"endLon"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Status string `json:"status"`
BlockchainID string `json:"blockchainId"`
ContractInfos []ContractInfo `json:"contractInfos"`
ProductID uint `json:"productId"`
MaxTemp float64 `json:"maxTemp"`
MinTemp float64 `json:"minTemp"`
ArrivalDate time.Time `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
BuyerName string `json:"buyerName" gorm:"-"`
NumberOfDevices int `json:"numberOfDevices" gorm:"-"`
}
func ConvertContractToResponse(contracts []Contract) []ContractResponse {
var contractResponses []ContractResponse
for _, contract := range contracts {
@@ -119,17 +119,18 @@ func ConvertContractToResponseModel(contracts []Contract) []ListContractResponse
}
listInvoiceResponse := ListContractResponse{
Status: KeyValue{Key: status.Key, Value: status.Value},
Buyer: CompanyShortResponse{ID: int(contract.BuyerID), Name: contract.BuyerName},
ContractID: int(contract.ID),
DateCreated: contract.CreatedAt,
NumberOfDevices: contract.NumberOfDevices,
Status: KeyValue{Key: status.Key, Value: status.Value},
Buyer: CompanyShortResponse{ID: int(contract.BuyerID), Name: contract.BuyerName},
ContractID: int(contract.ID),
DateCreated: contract.CreatedAt,
NumberOfDevices: contract.NumberOfDevices,
}
listInvoiceResponses = append(listInvoiceResponses, listInvoiceResponse)
}
return listInvoiceResponses
}
const ContractStatusActive = "active"
const ContractStatusPending = "pending"
const ContractStatusDraft = "draft"
@@ -138,14 +139,12 @@ const ContractStatusReadyForActivation = "ready_for_activation"
const ContractStatusExecuted = "executed"
const ContractStatusRevoked = "revoked"
const PenaltyTypeAmount = "amount"
const PenaltyTypePercentage = "percentage"
const PenaltyRecDaily = "daily"
const PenaltyRecMonthly = "monthly"
type Status struct {
Key string `json:"key"`
Value string `json:"value"`
@@ -164,25 +163,24 @@ func GetContractStatuses() []Status {
}
type ListContractResponse struct {
Status KeyValue `json:"status"`
Buyer CompanyShortResponse `json:"buyer"`
ContractID int `json:"contractID"`
NumberOfDevices int `json:"numberOfDevices"`
DateCreated time.Time `json:"dateCreated"`
Status KeyValue `json:"status"`
Buyer CompanyShortResponse `json:"buyer"`
ContractID int `json:"contractID"`
NumberOfDevices int `json:"numberOfDevices"`
DateCreated time.Time `json:"dateCreated"`
}
type CreateContractRequestPayload struct {
SellerID uint `json:"sellerId" binding:"required"`
BuyerID uint `json:"buyerId" binding:"required"`
Description string `json:"description"`
ProductID uint `json:"productId"`
MinTemp float64 `json:"minTemp" binding:"required"`
MaxTemp float64 `json:"maxTemp" binding:"required"`
ArrivalDate int64 `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
SellerID uint `json:"sellerId" binding:"required"`
BuyerID uint `json:"buyerId" binding:"required"`
Description string `json:"description"`
ProductID uint `json:"productId"`
MinTemp float64 `json:"minTemp" binding:"required"`
MaxTemp float64 `json:"maxTemp" binding:"required"`
ArrivalDate int64 `json:"arrivalDate"`
PenaltyType string `json:"penaltyType"`
PenaltyValue int `json:"penaltyValue"`
PenaltyRec string `json:"penaltyRec"`
}
func (Contract) Update() (bool, error) {
@@ -194,5 +192,3 @@ func (Contract) Create() (bool, error) {
func (Contract) Delete() (bool, error) {
return false, nil
}

6
models/location.go Normal file
View File

@@ -0,0 +1,6 @@
package models
type Place struct {
Text string `json:"text"`
Coordinates []float64 `json:"coordinates"`
}

View File

@@ -28,7 +28,10 @@ func RegisterPublicRoutes(r *gin.Engine) {
r.GET("/contracts/statuses", controllers.GetContractStatuses)
r.GET("/contracts", controllers.GetBuyerContracts)
r.POST("/contracts/create", controllers.CreateContract)
r.GET("/contracts/:contract_id", controllers.GetContractByID)
r.PATCH("/contracts/:contract_id", controllers.UpdateContract)
// Locations
r.GET("/locations", controllers.SearchPlace)
}

View File

@@ -0,0 +1,33 @@
package location
import (
"context"
"gitlab.com/pactual1/backend/models"
"gitlab.com/pactual1/backend/services/location/mapbox_lib"
)
type service struct {
accessToken string
}
type Service interface {
SearchPlace(context.Context, string) ([]*models.Place, error)
}
func NewService(accessToken string) Service {
return service{accessToken: accessToken}
}
func (s service) SearchPlace(ctx context.Context, query string) ([]*models.Place, error) {
geocoder := mapbox.NewFastHttpGeocoder(mapbox.AccessToken(s.accessToken))
resp, err := geocoder.ForwardGeocode(ctx, &mapbox.ForwardGeocodeRequest{SearchText: query})
if err != nil {
return nil, err
}
var places []*models.Place
for _, feature := range resp.Features {
places = append(places, &models.Place{Text: feature.Text, Coordinates: feature.Geometry.Coordinates})
}
return places, nil
}

View File

@@ -0,0 +1,7 @@
package mapbox
// Client covers all Mabpox API
type Client interface {
// Geocoder covers forward and reverse geocoding mapbox API
Geocoder
}

View File

@@ -0,0 +1,103 @@
package mapbox
import (
"context"
"os"
"github.com/valyala/fasthttp"
)
const (
defaultAPI = "https://api.mapbox.com"
)
// Option allows gradually modify config
type Option func(c config) config
type config struct {
accessToken string
rootAPI string
client FastHttpClient
logger Logger
// requestLogger will be called instead of testLogger if set.
requestLogger func(ctx context.Context) Logger
accessTokenGetValue []byte
geocodeEndpoint string
}
// withEnv overwrites config values with env is not empty
func (c config) withEnv() config {
at := os.Getenv("MAPBOX_ACCESS_TOKEN")
if at != "" {
c.accessToken = at
}
return c
}
// prepare prebuilds some reused api parts like access token http get value
func (c config) prepare() config {
c.accessTokenGetValue = []byte(questionMark + access_token + string(equalMark) + c.accessToken)
return c
}
func newConfig() config {
return config{
rootAPI: defaultAPI,
client: &fasthttp.Client{},
geocodeEndpoint: "mapbox.places",
}
}
// Log used to debug traces and to log errors.
func Log(l Logger) Option {
return func(c config) config {
c.logger = l
return c
}
}
// RequestLogger sets the way testLogger could be extracted from request context.
// If set will be used instead of Log.
func RequestLogger(extract func(ctx context.Context) Logger) Option {
return func(c config) config {
c.requestLogger = extract
return c
}
}
// AccessToken sets access_token get param.
// Could be set with MAPBOX_ACCESS_TOKEN too.
func AccessToken(at string) Option {
return func(c config) config {
c.accessToken = at
return c
}
}
// RootAPI allows to change root api address.
// default to https://api.mapbox.com
func RootAPI(rootAPI string) Option {
return func(c config) config {
c.rootAPI = rootAPI
return c
}
}
// HttpClient allows to change default fast http client
func HttpClient(c FastHttpClient) Option {
return func(fhc config) config {
fhc.client = c
return fhc
}
}
// GeocodeEndpoint sets geocode endpoint.
// could be set to mapbox.places-permanent, defualt to mapbox.places
func GeocodeEndpoint(endpoint string) Option {
return func(c config) config {
c.geocodeEndpoint = endpoint
return c
}
}

View File

@@ -0,0 +1,35 @@
package mapbox
type (
Feature struct {
ID string `json:"id"`
Type string `json:"type"`
PlaceType []string `json:"place_type"`
Relevance float64 `json:"relevance"`
Properties Properties `json:"properties"`
Text string `json:"text"`
PlaceName string `json:"place_name"`
Center []float64 `json:"center"`
Geometry Geometry `json:"geometry"`
Address string `json:"address"`
Context []Context `json:"context"`
BoundingBox []float64 `json:"bbox"`
}
Properties struct {
Accuracy string `json:"accuracy"`
ShortCode string `json:"short_code"`
}
Geometry struct {
Type string `json:"type"`
Coordinates []float64 `json:"coordinates"`
}
Context struct {
ID string `json:"id"`
Text string `json:"text"`
Wikidata string `json:"wikidata"`
ShortCode string `json:"short_code"`
}
)

View File

@@ -0,0 +1,555 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package mapbox
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox(in *jlexer.Lexer, out *Properties) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "accuracy":
out.Accuracy = string(in.String())
case "short_code":
out.ShortCode = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox(out *jwriter.Writer, in Properties) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"accuracy\":"
out.RawString(prefix[1:])
out.String(string(in.Accuracy))
}
{
const prefix string = ",\"short_code\":"
out.RawString(prefix)
out.String(string(in.ShortCode))
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v Properties) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Properties) MarshalEasyJSON(w *jwriter.Writer) {
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Properties) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Properties) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox(l, v)
}
func easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox1(in *jlexer.Lexer, out *Geometry) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "type":
out.Type = string(in.String())
case "coordinates":
if in.IsNull() {
in.Skip()
out.Coordinates = nil
} else {
in.Delim('[')
if out.Coordinates == nil {
if !in.IsDelim(']') {
out.Coordinates = make([]float64, 0, 8)
} else {
out.Coordinates = []float64{}
}
} else {
out.Coordinates = (out.Coordinates)[:0]
}
for !in.IsDelim(']') {
var v1 float64
v1 = float64(in.Float64())
out.Coordinates = append(out.Coordinates, v1)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox1(out *jwriter.Writer, in Geometry) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"type\":"
out.RawString(prefix[1:])
out.String(string(in.Type))
}
{
const prefix string = ",\"coordinates\":"
out.RawString(prefix)
if in.Coordinates == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v2, v3 := range in.Coordinates {
if v2 > 0 {
out.RawByte(',')
}
out.Float64(float64(v3))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v Geometry) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox1(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Geometry) MarshalEasyJSON(w *jwriter.Writer) {
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox1(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Geometry) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox1(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Geometry) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox1(l, v)
}
func easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox2(in *jlexer.Lexer, out *Feature) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "id":
out.ID = string(in.String())
case "type":
out.Type = string(in.String())
case "place_type":
if in.IsNull() {
in.Skip()
out.PlaceType = nil
} else {
in.Delim('[')
if out.PlaceType == nil {
if !in.IsDelim(']') {
out.PlaceType = make([]string, 0, 4)
} else {
out.PlaceType = []string{}
}
} else {
out.PlaceType = (out.PlaceType)[:0]
}
for !in.IsDelim(']') {
var v4 string
v4 = string(in.String())
out.PlaceType = append(out.PlaceType, v4)
in.WantComma()
}
in.Delim(']')
}
case "relevance":
out.Relevance = float64(in.Float64())
case "properties":
(out.Properties).UnmarshalEasyJSON(in)
case "text":
out.Text = string(in.String())
case "place_name":
out.PlaceName = string(in.String())
case "center":
if in.IsNull() {
in.Skip()
out.Center = nil
} else {
in.Delim('[')
if out.Center == nil {
if !in.IsDelim(']') {
out.Center = make([]float64, 0, 8)
} else {
out.Center = []float64{}
}
} else {
out.Center = (out.Center)[:0]
}
for !in.IsDelim(']') {
var v5 float64
v5 = float64(in.Float64())
out.Center = append(out.Center, v5)
in.WantComma()
}
in.Delim(']')
}
case "geometry":
(out.Geometry).UnmarshalEasyJSON(in)
case "address":
out.Address = string(in.String())
case "context":
if in.IsNull() {
in.Skip()
out.Context = nil
} else {
in.Delim('[')
if out.Context == nil {
if !in.IsDelim(']') {
out.Context = make([]Context, 0, 1)
} else {
out.Context = []Context{}
}
} else {
out.Context = (out.Context)[:0]
}
for !in.IsDelim(']') {
var v6 Context
(v6).UnmarshalEasyJSON(in)
out.Context = append(out.Context, v6)
in.WantComma()
}
in.Delim(']')
}
case "bbox":
if in.IsNull() {
in.Skip()
out.BoundingBox = nil
} else {
in.Delim('[')
if out.BoundingBox == nil {
if !in.IsDelim(']') {
out.BoundingBox = make([]float64, 0, 8)
} else {
out.BoundingBox = []float64{}
}
} else {
out.BoundingBox = (out.BoundingBox)[:0]
}
for !in.IsDelim(']') {
var v7 float64
v7 = float64(in.Float64())
out.BoundingBox = append(out.BoundingBox, v7)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox2(out *jwriter.Writer, in Feature) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"id\":"
out.RawString(prefix[1:])
out.String(string(in.ID))
}
{
const prefix string = ",\"type\":"
out.RawString(prefix)
out.String(string(in.Type))
}
{
const prefix string = ",\"place_type\":"
out.RawString(prefix)
if in.PlaceType == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v8, v9 := range in.PlaceType {
if v8 > 0 {
out.RawByte(',')
}
out.String(string(v9))
}
out.RawByte(']')
}
}
{
const prefix string = ",\"relevance\":"
out.RawString(prefix)
out.Float64(float64(in.Relevance))
}
{
const prefix string = ",\"properties\":"
out.RawString(prefix)
(in.Properties).MarshalEasyJSON(out)
}
{
const prefix string = ",\"text\":"
out.RawString(prefix)
out.String(string(in.Text))
}
{
const prefix string = ",\"place_name\":"
out.RawString(prefix)
out.String(string(in.PlaceName))
}
{
const prefix string = ",\"center\":"
out.RawString(prefix)
if in.Center == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v10, v11 := range in.Center {
if v10 > 0 {
out.RawByte(',')
}
out.Float64(float64(v11))
}
out.RawByte(']')
}
}
{
const prefix string = ",\"geometry\":"
out.RawString(prefix)
(in.Geometry).MarshalEasyJSON(out)
}
{
const prefix string = ",\"address\":"
out.RawString(prefix)
out.String(string(in.Address))
}
{
const prefix string = ",\"context\":"
out.RawString(prefix)
if in.Context == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v12, v13 := range in.Context {
if v12 > 0 {
out.RawByte(',')
}
(v13).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"bbox\":"
out.RawString(prefix)
if in.BoundingBox == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v14, v15 := range in.BoundingBox {
if v14 > 0 {
out.RawByte(',')
}
out.Float64(float64(v15))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v Feature) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox2(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Feature) MarshalEasyJSON(w *jwriter.Writer) {
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox2(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Feature) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox2(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Feature) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox2(l, v)
}
func easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox3(in *jlexer.Lexer, out *Context) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "id":
out.ID = string(in.String())
case "text":
out.Text = string(in.String())
case "wikidata":
out.Wikidata = string(in.String())
case "short_code":
out.ShortCode = string(in.String())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox3(out *jwriter.Writer, in Context) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"id\":"
out.RawString(prefix[1:])
out.String(string(in.ID))
}
{
const prefix string = ",\"text\":"
out.RawString(prefix)
out.String(string(in.Text))
}
{
const prefix string = ",\"wikidata\":"
out.RawString(prefix)
out.String(string(in.Wikidata))
}
{
const prefix string = ",\"short_code\":"
out.RawString(prefix)
out.String(string(in.ShortCode))
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v Context) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox3(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v Context) MarshalEasyJSON(w *jwriter.Writer) {
easyjson3e8ab7adEncodeGithubComHumansNetMapboxSdkGoMapbox3(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *Context) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox3(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *Context) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson3e8ab7adDecodeGithubComHumansNetMapboxSdkGoMapbox3(l, v)
}

View File

@@ -0,0 +1,421 @@
package mapbox
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/pkg/errors"
"github.com/valyala/fasthttp"
)
const (
limit = "limit"
types = "types"
country = "country"
language = "language"
reverseMode = "reverseMode"
autocomplete = "autocomplete"
fuzzymatch = "fuzzymatch"
bbox = "bbox"
proximity = "proximity"
routing = "routing"
trueStr = "true"
oneStr = "1"
access_token = "access_token"
floatFormatNoExponent = 'f'
respHeaderRateLimitInterval = "X-Rate-Limit-Interval"
respHeaderRateLimitLimit = "X-Rate-Limit-Limit"
respHeaderRateLimitReset = "X-Rate-Limit-Reset"
)
var (
responseFormatJSON = []byte(".json")
getMethod = []byte("GET")
)
type GeoPoint struct {
Lon float64
Lat float64
}
type ReverseGeocodeRequest struct {
GeoPoint GeoPoint
// Limit results to one or more countries.
Limit int
// Filter results to include only a subset (one or more) of the available feature types.
// Options are country, region, postcode, district, place, locality, neighborhood, address, and poi.
// Multiple options can be comma-separated. Note that poi.landmark is a deprecated type that, while still supported,
// returns the same data as is returned using the poi type.
Types []string
// Permitted values are ISO 3166 alpha 2(https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes separated by commas.
Country string
// Specify the users language. This parameter controls the language of the text supplied in responses.
// Options are IETF language tags comprised of a mandatory ISO 639-1 language code and, optionally,
// one or more IETF subtags for country or script.
// More than one value can also be specified, separated by commas,
// for applications that need to display labels in multiple languages.
// For more information on which specific languages are supported, see https://docs.mapbox.com/api/search/#language-coverage
Language string
// Decides how results are sorted in a reverse geocoding query
// if multiple results are requested using a limit other than 1.
// Options are distance (default), which causes the closest feature
// to always be returned first, and score, which allows high-prominence features
// to be sorted higher than nearer, lower-prominence features.
ReverseMode int
// Specify whether to request additional metadata about the recommended navigation destination corresponding
// to the feature (true) or not (false, default). Only applicable for address features.
// For example, if routing=true the response could include data about a point on the road the feature fronts.
// Response features may include an array containing one or more routable points.
// Routable points cannot always be determined.
// Consuming applications should fall back to using the features normal geometry for routing
// if a separate routable point is not returned.
Routing bool
}
// RateLimit wraps mapbox API rate limit resp headers
type RateLimit struct {
Interval []byte
Limit []byte
Reset []byte
}
// easyjson:json
type rawReverseGeoResp struct {
Features []Feature `json:"features"`
Query []float64 `json:"query"`
}
// easyjson:json
type rawForwardGeoResp struct {
Features []Feature `json:"features"`
Query []string `json:"query"`
}
// GeocodeResponse
type GeocodeResponse struct {
RateLimit RateLimit
// Raw mapbox API response
RawResp []byte
// passed query to mapbox
ReverseQuery GeoPoint
ForwardQuery []string
// response result type
Type string
// response data
Features []Feature
}
type ForwardGeocodeRequest struct {
//The feature youre trying to look up.
//This could be an address, a point of interest name, a city name, etc.
//When searching for points of interest, it can also be a category name (for example, “coffee shop”).
//For information on categories, see the Point of interest category coverage section.
//The search text should be expressed as a URL-encoded UTF-8 string,
//and must not contain the semicolon character (either raw or URL-encoded).
//Your search text, once decoded, must consist of at most 20 words and numbers separated by spacing and punctuation,
//and at most 256 characters.
//
//The accuracy of coordinates returned by a forward geocoding request can be impacted
//by how the addresses in the query are formatted. Learn more about address formatting
//best practices in the https://docs.mapbox.com/help/troubleshooting/address-geocoding-format-guide.
SearchText string
//Specify whether to return autocomplete results (true, default) or not (false).
//When autocomplete is enabled, results will be included that start with the requested string,
//rather than just responses that match it exactly.
//For example, a query for India might return both India and Indiana with autocomplete enabled,
//but only India if its disabled.
//
//When autocomplete is enabled, each user keystroke counts as one request to the Geocoding API.
//For example, a search for "coff" would be reflected as four separate Geocoding API requests.
//To reduce the total requests sent, you can configure your application
//to only call the Geocoding API after a specific number of characters are typed.
Autocomplete *bool // default true
//Limit results to only those contained within the supplied bounding box
//Bounding boxes should be supplied as four numbers separated by commas,
//in minLon,minLat,maxLon,maxLat order.
//The bounding box cannot cross the 180th meridian.
Bbox []float64
//Limit results to one or more countries.
//Permitted values are ISO 3166 alpha 2 country codes separated by commas.
Country string
//Specify whether the Geocoding API should attempt approximate,
//as well as exact, matching when performing searches (true, default),
//or whether it should opt out of this behavior and only attempt exact matching (false).
//For example, the default setting might return Washington, DC for a query of wahsington,
//even though the query was misspelled.
FuzzyMatch *bool // default true
//Specify the users language.
//This parameter controls the language of the text supplied in responses, and also affects result scoring,
//with results matching the users query in the requested language being preferred over results
//that match in another language. For example, an autocomplete query for things
//that start with Frank might return Frankfurt as the first result with an English (en) language parameter,
//but Frankreich (“France”) with a German (de) language parameter.
//
//Options are IETF language tags comprised of a mandatory ISO 639-1 language code and, optionally,
//one or more IETF subtags for country or script.
//
//More than one value can also be specified, separated by commas,
//for applications that need to display labels in multiple languages.
//
//For more information on which specific languages are supported, see the https://docs.mapbox.com/api/search/#language-coverage.
Language string
//Specify the maximum number of results to return. The default is 5 and the maximum supported is 10.
Limit int // default 5
//Bias the response to favor results that are closer to this location
Proximity *GeoPoint
//Specify whether to request additional metadata about the recommended navigation destination
//corresponding to the feature (true) or not (false, default). Only applicable for address features.
//
//For example, if routing=true the response could include data about a point on the road the feature fronts.
//Response features may include an array containing one or more routable points.
//Routable points cannot always be determined.
//Consuming applications should fall back to using the features normal geometry for routing
//if a separate routable point is not returned.
Routing bool //default false
//Filter results to include only a subset (one or more) of the available feature types.
//Options are country, region, postcode, district, place, locality, neighborhood, address, and poi.
//Multiple options can be comma-separated. Note that poi.landmark is a deprecated type that,
//while still supported, returns the same data as is returned using the poi type.
//
//For more information on the available types, see the https://docs.mapbox.com/api/search/#data-types.
Types []string
}
// Geocoder encapsulates forward and reverse geocode calls.
type Geocoder interface {
// ReverseGeocode calls geocode/v5 reverse mapbox API
ReverseGeocode(ctx context.Context, req *ReverseGeocodeRequest) (*GeocodeResponse, error)
// ReverseGeocode calls geocode/v5 reverse mapbox API
ForwardGeocode(ctx context.Context, req *ForwardGeocodeRequest) (*GeocodeResponse, error)
}
// FastHttpGeocoder is a fasthttp Geocoder implementation
type FastHttpGeocoder struct {
config
geocodeAPIURL []byte
stringBufPull *stringsBufferPool
}
// ReverseGeocode calls geocode/v5 reverse mapbox API thought fasthttp client.
func (c *FastHttpGeocoder) ReverseGeocode(ctx context.Context, req *ReverseGeocodeRequest) (*GeocodeResponse, error) {
freq := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(freq)
fresp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(fresp)
// split multivalues to limit memory consumption
values := make(map[string]string, 5)
if req.Country != "" {
values[country] = req.Country
}
if req.Limit != 0 {
values[limit] = strconv.Itoa(req.Limit)
}
if req.Language != "" {
values[language] = req.Language
}
if req.Routing {
values[routing] = trueStr
}
if req.ReverseMode == 1 {
values[reverseMode] = oneStr
}
if len(req.Types) > 0 {
values[types] = strings.Join(req.Types, ",")
}
buf := c.stringBufPull.acquireStringsBuilder()
defer c.stringBufPull.releaseStringsBuilder(buf)
buf.Write(c.geocodeAPIURL)
buf.WriteString(strconv.FormatFloat(req.GeoPoint.Lon, floatFormatNoExponent, 6, 64))
buf.WriteByte(comma)
buf.WriteString(strconv.FormatFloat(req.GeoPoint.Lat, floatFormatNoExponent, 6, 64))
buf.Write(responseFormatJSON)
buf.Write(c.accessTokenGetValue)
encodeValues(buf, values)
reqURI := buf.Bytes()
c.withLogger(ctx, func(logger Logger) {
logger.Debugf("mapbox_sdk: reverse geocode request %s", buf.String())
})
freq.Header.SetMethodBytes(getMethod)
freq.SetRequestURIBytes(reqURI)
if err := c.client.Do(freq, fresp); err != nil {
return nil, err
}
respBytes := make([]byte, len(fresp.Body()))
copy(respBytes, fresp.Body())
c.withLogger(ctx, func(logger Logger) {
logger.Debugf("mapbox_sdk: reverse geocode response %s", string(respBytes))
})
if fresp.Header.StatusCode() != http.StatusOK {
return nil, errors.Errorf("failed to reverse geocode URI %s statusCode %d resp %s",
reqURI, fresp.Header.StatusCode(), string(respBytes))
}
respRaw := rawReverseGeoResp{}
if err := respRaw.UnmarshalJSON(respBytes); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshall raw reverse geocode resp %s", string(respBytes))
}
if len(respRaw.Query) != 2 {
return nil, errors.Errorf("unexpected len of query coordinates in resp %s", string(respBytes))
}
return &GeocodeResponse{
RateLimit: readRespRateLimit(fresp),
RawResp: respBytes,
ReverseQuery: GeoPoint{
Lon: respRaw.Query[0],
Lat: respRaw.Query[1],
},
Features: respRaw.Features,
}, nil
}
// ReverseGeocode calls geocode/v5 reverse mapbox API thought fasthttp client.
func (c *FastHttpGeocoder) ForwardGeocode(ctx context.Context, req *ForwardGeocodeRequest) (*GeocodeResponse, error) {
freq := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(freq)
fresp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(fresp)
// split multivalues to limit memory consumption
values := make(map[string]string, 9)
if req.Country != "" {
values[country] = req.Country
}
if req.Limit != 0 {
values[limit] = strconv.Itoa(req.Limit)
}
if req.Language != "" {
values[language] = req.Language
}
if req.Routing {
values[routing] = trueStr
}
if req.Autocomplete != nil {
values[autocomplete] = fmt.Sprint(*req.Autocomplete)
} else {
values[autocomplete] = trueStr
}
if req.FuzzyMatch != nil {
values[fuzzymatch] = fmt.Sprint(*req.FuzzyMatch)
} else {
values[fuzzymatch] = trueStr
}
if len(req.Bbox) == 4 {
values[bbox] = fmt.Sprintf("%f,%f,%f,%f", req.Bbox[0], req.Bbox[1], req.Bbox[2], req.Bbox[3])
}
if req.Proximity != nil {
values[proximity] = fmt.Sprintf("%f,%f", req.Proximity.Lon, req.Proximity.Lat)
}
values[routing] = fmt.Sprint(req.Routing)
if len(req.Types) > 0 {
values[types] = strings.Join(req.Types, ",")
}
buf := c.stringBufPull.acquireStringsBuilder()
defer c.stringBufPull.releaseStringsBuilder(buf)
buf.Write(c.geocodeAPIURL)
buf.WriteString(req.SearchText)
buf.Write(responseFormatJSON)
buf.Write(c.accessTokenGetValue)
encodeValues(buf, values)
reqURI := buf.Bytes()
c.withLogger(ctx, func(logger Logger) {
logger.Debugf("mapbox_sdk: forward geocode request %s", buf.String())
})
freq.Header.SetMethodBytes(getMethod)
freq.SetRequestURIBytes(reqURI)
if err := c.client.Do(freq, fresp); err != nil {
return nil, err
}
respBytes := make([]byte, len(fresp.Body()))
copy(respBytes, fresp.Body())
c.withLogger(ctx, func(logger Logger) {
logger.Debugf("mapbox_sdk: forward geocode response %s", string(respBytes))
})
if fresp.Header.StatusCode() != http.StatusOK {
return nil, errors.Errorf("failed to reverse geocode URI %s statusCode %d resp %s",
reqURI, fresp.Header.StatusCode(), string(respBytes))
}
respRaw := rawForwardGeoResp{}
if err := respRaw.UnmarshalJSON(respBytes); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshall raw reverse geocode resp %s", string(respBytes))
}
return &GeocodeResponse{
RateLimit: readRespRateLimit(fresp),
RawResp: respBytes,
Features: respRaw.Features,
ForwardQuery: respRaw.Query,
}, nil
}
func NewFastHttpGeocoder(opts ...Option) *FastHttpGeocoder {
c := FastHttpGeocoder{
config: newConfig(),
stringBufPull: newStringsBufferPool(),
geocodeAPIURL: []byte("/geocoding/v5/"),
}
for _, o := range opts {
c.config = o(c.config)
}
c.config = c.config.withEnv()
c.config = c.config.prepare()
c.geocodeAPIURL = []byte(c.rootAPI + string(c.geocodeAPIURL) + c.geocodeEndpoint + slash)
return &c
}
func readRespRateLimit(resp *fasthttp.Response) RateLimit {
return RateLimit{
Interval: resp.Header.Peek(respHeaderRateLimitInterval),
Limit: resp.Header.Peek(respHeaderRateLimitLimit),
Reset: resp.Header.Peek(respHeaderRateLimitReset),
}
}

View File

@@ -0,0 +1,293 @@
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package mapbox
import (
json "encoding/json"
easyjson "github.com/mailru/easyjson"
jlexer "github.com/mailru/easyjson/jlexer"
jwriter "github.com/mailru/easyjson/jwriter"
)
// suppress unused package warning
var (
_ *json.RawMessage
_ *jlexer.Lexer
_ *jwriter.Writer
_ easyjson.Marshaler
)
func easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox(in *jlexer.Lexer, out *rawReverseGeoResp) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "features":
if in.IsNull() {
in.Skip()
out.Features = nil
} else {
in.Delim('[')
if out.Features == nil {
if !in.IsDelim(']') {
out.Features = make([]Feature, 0, 1)
} else {
out.Features = []Feature{}
}
} else {
out.Features = (out.Features)[:0]
}
for !in.IsDelim(']') {
var v1 Feature
(v1).UnmarshalEasyJSON(in)
out.Features = append(out.Features, v1)
in.WantComma()
}
in.Delim(']')
}
case "query":
if in.IsNull() {
in.Skip()
out.Query = nil
} else {
in.Delim('[')
if out.Query == nil {
if !in.IsDelim(']') {
out.Query = make([]float64, 0, 8)
} else {
out.Query = []float64{}
}
} else {
out.Query = (out.Query)[:0]
}
for !in.IsDelim(']') {
var v2 float64
v2 = float64(in.Float64())
out.Query = append(out.Query, v2)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox(out *jwriter.Writer, in rawReverseGeoResp) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"features\":"
out.RawString(prefix[1:])
if in.Features == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v3, v4 := range in.Features {
if v3 > 0 {
out.RawByte(',')
}
(v4).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"query\":"
out.RawString(prefix)
if in.Query == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v5, v6 := range in.Query {
if v5 > 0 {
out.RawByte(',')
}
out.Float64(float64(v6))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v rawReverseGeoResp) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v rawReverseGeoResp) MarshalEasyJSON(w *jwriter.Writer) {
easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *rawReverseGeoResp) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *rawReverseGeoResp) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox(l, v)
}
func easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox1(in *jlexer.Lexer, out *rawForwardGeoResp) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeString()
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "features":
if in.IsNull() {
in.Skip()
out.Features = nil
} else {
in.Delim('[')
if out.Features == nil {
if !in.IsDelim(']') {
out.Features = make([]Feature, 0, 1)
} else {
out.Features = []Feature{}
}
} else {
out.Features = (out.Features)[:0]
}
for !in.IsDelim(']') {
var v7 Feature
(v7).UnmarshalEasyJSON(in)
out.Features = append(out.Features, v7)
in.WantComma()
}
in.Delim(']')
}
case "query":
if in.IsNull() {
in.Skip()
out.Query = nil
} else {
in.Delim('[')
if out.Query == nil {
if !in.IsDelim(']') {
out.Query = make([]string, 0, 4)
} else {
out.Query = []string{}
}
} else {
out.Query = (out.Query)[:0]
}
for !in.IsDelim(']') {
var v8 string
v8 = string(in.String())
out.Query = append(out.Query, v8)
in.WantComma()
}
in.Delim(']')
}
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox1(out *jwriter.Writer, in rawForwardGeoResp) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"features\":"
out.RawString(prefix[1:])
if in.Features == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v9, v10 := range in.Features {
if v9 > 0 {
out.RawByte(',')
}
(v10).MarshalEasyJSON(out)
}
out.RawByte(']')
}
}
{
const prefix string = ",\"query\":"
out.RawString(prefix)
if in.Query == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
out.RawString("null")
} else {
out.RawByte('[')
for v11, v12 := range in.Query {
if v11 > 0 {
out.RawByte(',')
}
out.String(string(v12))
}
out.RawByte(']')
}
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v rawForwardGeoResp) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox1(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v rawForwardGeoResp) MarshalEasyJSON(w *jwriter.Writer) {
easyjson46e924aeEncodeGithubComHumansNetMapboxSdkGoMapbox1(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *rawForwardGeoResp) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox1(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *rawForwardGeoResp) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjson46e924aeDecodeGithubComHumansNetMapboxSdkGoMapbox1(l, v)
}

View File

@@ -0,0 +1,9 @@
package mapbox
import (
"github.com/valyala/fasthttp"
)
type FastHttpClient interface {
Do(req *fasthttp.Request, resp *fasthttp.Response) error
}

View File

@@ -0,0 +1,22 @@
package mapbox
import (
"context"
)
type Logger interface {
Debugf(msg string, params ...interface{})
Errorf(msg string, params ...interface{})
}
// withLogger helps to reduce unnecessary allocations
func (c *config) withLogger(ctx context.Context, do func(Logger)) {
if c.requestLogger != nil {
do(c.requestLogger(ctx))
return
}
if c.logger != nil {
do(c.logger)
}
}

View File

@@ -0,0 +1,31 @@
package mapbox
import (
"bytes"
"sync"
)
type noCopy struct{}
func (*noCopy) Lock() {}
func (*noCopy) Unlock() {}
type stringsBufferPool struct {
noCopy noCopy
p sync.Pool
}
func newStringsBufferPool() *stringsBufferPool {
return &stringsBufferPool{p: sync.Pool{New: func() interface{} {
return &bytes.Buffer{}
}}}
}
func (pool *stringsBufferPool) acquireStringsBuilder() *bytes.Buffer {
return pool.p.Get().(*bytes.Buffer)
}
func (pool *stringsBufferPool) releaseStringsBuilder(b *bytes.Buffer) {
b.Reset()
pool.p.Put(b)
}

View File

@@ -0,0 +1,23 @@
package mapbox
import (
"bytes"
)
const (
slash = "/"
comma = ','
questionMark = "?"
equalMark = '='
ampersandMark = '&'
)
// encodeValues do almost the same as url.Values.Encode() but faster and reuses *strings.Builder
func encodeValues(buf *bytes.Buffer, values map[string]string) {
for k, v := range values {
buf.WriteByte(ampersandMark)
buf.WriteString(k)
buf.WriteByte(equalMark)
buf.WriteString(v)
}
}