Couldn't find any existing Go specific answer so creating a new one.
Our datastore consist of following columns/attributes
Name/ID Email UserID UserName
Now, I want to retrieve these values into my Go struct. Here is what i have written
type UserDetails struct {
NameID string
Email string `datastore:"Email"`
UserID string `datastore:"UserID"`
UserName string `datastore:UserName`
}
Now, when i am fetching this entity (based on kind), I am manually setting the NameID i.e.,
func (c DataStoreClient) GetUserDetailsByOrg(ctx context.Context, orgName string) ([]*UserDetails, error) {
var userDetails []*UserDetails
q := datastore.NewQuery(userDetailsKind).
Namespace(orgName)
keys, err := c.client.GetAll(ctx, q, &userDetails)
for i, key := range keys {
userDetails[i].NameID = key.Name
}
return userDetails, err
}
Now, the question that i want to ask that is this the correct approach to retrieve Name/ID or can i retrieve it by specifying datastore:Name/ID in struct?
Implement the KeyLoader interface to set the field during entity load.
type UserDetails struct {
NameID string `datastore:"-"`
Email string `datastore:"Email"`
UserID string `datastore:"UserID"`
UserName string `datastore:UserName`
}
func (ud *UserDetails) LoadKey(k *datastore.Key) error {
ud.NameID = k.Name
return nil
}
func (ud *UserDetails) Load(ps []datastore.Property) error {
return datastore.LoadStruct(ud, ps)
}
func (ud *UserDetails) Save() ([]datastore.Property, error) {
return datastore.SaveStruct(ud)
}
Use it like this:
func (c DataStoreClient) GetUserDetailsByOrg(ctx context.Context, orgName string) ([]*UserDetails, error) {
var userDetails []*UserDetails
q := datastore.NewQuery(userDetailsKind).
Namespace(orgName)
_, err := c.client.GetAll(ctx, q, &userDetails)
return userDetails, err
}
The approach in the question works. The advantage of the KeyLoader implementation is that it saves a couple of lines of code wherever the application queries for or gets an entity.
Set the NameID datastore name to "-" so that the property loader and saver ignores the field.
Your approach is fine. However, because you have NameID without any tag, there will be a datastore entity property with that name set to nothing. It's an unnecessary property to store. So, you could make it "NameID string datastore:"-" so datastore ignores it. Alternatively, if you are not exporting this field outside the package or via JSON, you can use a lower case name like "nameID".
Another approach you have is to automatically get the entire key by having a "key" field. You can learn more about it in Key Field
https://godoc.org/cloud.google.com/go/datastore#hdr-Key_Field
Related
I am trying to implement the Value interfaces for a db model, however I only receive an empty object when the Value function is called. The models are stored in the table as Link's but when I read them I want to append an additional field by using the value interface, the function is called however I'm not sure how I can access the original entry so that I can append the name to it
I define my struct as such:
type Link struct {
Id string
Url string
}
type ReturnLink struct {
Link
Name string
}
func (l *Link) Scan(value interface{}) error {
fmt.Println("scan")
return nil
}
func (l Link) Value() (driver.Value, error) {
fmt.Println("value")
name := getName()
returnLink := ReturnLink{
Link: l,
Name: name,
}
return returnLink, nil
}
Once I have formed the query, I am able to scan the value into the struct:
var link ReturnLink
db.Model(&Link{}).Where("id = ?", id).Scan(&link)
This returns a correct link with an empty name when I don't define the Value interface, but once implemented it only returns null values for each field, is there something I'm missing?
I have also tried not implementing the scan interface but it changes nothing
I have a datastore table, which is like that
Name/ID | UserEmail | UserRole | UserPermissions
------------------------------------------------------
The UserRole attribute in json is a string. However, in Go code, it's a type
type UserDetails struct {
NameID string
UserEmail string
UserRole UserType
UserPermissions string //json??
}
type UserType string
const (
UnknownUserRole UserType = "UNKNOWN"
SiteAdmin UserType = "SITE_ADMIN"
SiteHR UserType = "SITE_HR"
)
func (ut *UserType) String() string {
return string(*ut)
}
func UserTypeFromString(userType string) UserType {
switch userType {
case "SITE_ADMIN":
return SiteAdmin
case "SITE_HR":
return SiteHR
default:
return UnknownRole
}
}
Now, I have to read all users for given org. I am using this code to do so
func (c DataStoreClient) GetUserDetailsByOrg(ctx context.Context, orgName string) ([]*UserDetails, error) {
var userDetails []*UserDetails
q := datastore.NewQuery(userDetailsKind).
Namespace(orgName)
keys, err := c.client.GetAll(ctx, q, &userDetails)
for i, key := range keys {
userDetails[i].NameID = key.Name
}
return userDetails, err
}
How can i read UserType from datastore using above code into UserDetails.UserType enum?
The code in the question works as is. There's no need to implement PropertyLoadSaver or loop over returned entities as suggested in other answers.
The custom data type is a string. The datastore package marshals all string types to and from datastore strings. It just works.
What you're essentially doing with UserType is that you have two valid values, and you're mapping all other values to a common unknown value. The following should work:
for i, key := range keys {
userDetails[i].NameID = key.Name
userDetails[i].UserRole = UserTypeFromString( string(userDetails[i].UserRole))
}
If you make your struct implement PropertyLoadSaver interface
https://godoc.org/cloud.google.com/go/datastore#hdr-The_PropertyLoadSaver_Interface
then you can handle these special requirements. You wouldn't have to remember to post process with every get/query.
Using a struct, I'm saving the data in the JSON form to collection like so:
type SignUp struct {
Id int `json:"_id" bson:"_id"`
UserName string `json:"user_name" bson:"user_name"`
EmailId string `json:"email_id" bson:"email_id"`
Password string `json:"password" bson:"password"`
}
type SignUps []SignUp
And retrieved the data from the collection of mongodb using function
func Login(c *gin.Context) {
response := ResponseControllerList{}
conditions := bson.M{}
data, err := GetAllUser(conditions)
fmt.Println(data)
fmt.Println(err)
Matching(data)
return
}
func GetAllUser(blogQuery interface{}) (result SignUps, err error) {
mongoSession := config.ConnectDb()
sessionCopy := mongoSession.Copy()
defer sessionCopy.Close()
getCollection := mongoSession.DB("sign").C("signup")
err = getCollection.Find(blogQuery).Select(bson.M{}).All(&result)
if err != nil {
return result, err
}
return result, nil
}
But the data retrieved without its keys like shown below:-
[{1 puneet puneet#gmail.com puneet} {2 Rohit abc#gmail.com puneet}]
Issue is that I want to collect all the existing EmailId and Password
and match them with userid and password entered by the user in fucntion given bellow:-
func Matching(data Signups){
userid="abc#gmail.com"
password="puneet"
//match?
}
How can I match the userid and password with the EmailId and Password fields?
This is not answering your question directly, but is very important nonetheless. It seems that you are saving the passwords of your users in plain text in the database. This is very careless and you should add some encryption like bcrypt: https://godoc.org/golang.org/x/crypto/bcrypt
When you have added some encryption, your matching would be quite different than all given answers. This depends on the used encryption package. But please ignore the other answers. They might work, but only for plain text passwords.
You can give an if condition to check if the userid or email is empty or not on your Matching function like:
....
if data.userid != "" {
data.username = data.userid
}
data.password
....
You can create new struct for the query and use it to create your Matching function.
for _, signup := range data {
if userId == signup.Id && password == signup.Password {
println("Match!")
}
}
I have the following struct:
type Record struct {
Id string `json:"id"`
ApiKey string `json:"apiKey"`
Body []string `json:"body"`
Type string `json:"type"`
}
Which is a Blueprint of a dynamoDB table. And I need somehow, delete the ApiKey, after using to check if the user has access to the giving record. Explaining:
I have and endpoint in my API where the user can send a id to get a item, but he needs to have access to the ID and the ApiKey (I'm using Id (uuid) + ApiKey) to create unique items.
How I'm doing:
func getExtraction(id string, apiKey string) (Record, error) {
svc := dynamodb.New(cfg)
req := svc.GetItemRequest(&dynamodb.GetItemInput{
TableName: aws.String(awsEnv.Dynamo_Table),
Key: map[string]dynamodb.AttributeValue{
"id": {
S: aws.String(id),
},
},
})
result, err := req.Send()
if err != nil {
return Record{}, err
}
record := Record{}
err = dynamodbattribute.UnmarshalMap(result.Item, &record)
if err != nil {
return Record{}, err
}
if record.ApiKey != apiKey {
return Record{}, fmt.Errorf("item %d not found", id)
}
// Delete ApiKey from record
return record, nil
}
After checking if the ApiKey is equal to the provided apiKey, I want to delete the ApiKey from record, but unfortunately that's not possible using delete.
Thank you.
There is no way to actually edit a golang type (such as a struct) at runtime. Unfortunately you haven't really explained what you are hoping to achieve by "deleting" the APIKey field.
General approaches would be:
set the APIKey field to an empty string after inspection, if you dont want to display this field when empty set the json struct tag to omitempty (e.g `json:"apiKey,omitempty"`)
set the APIKey field to never Marshal into JSON ( e.g ApiKey string `json:"-"`) and you will still be able to inspect it just wont display in JSON, you could take this further by adding a custom marshal / unmarshal function to handle this in one direction or in a context dependent way
copy the data to a new struct, e.g type RecordNoAPI struct without the APIKey field and return that after you have inspected the original Record
Created RecordShort structure without "ApiKey"
Marshalin Record
Unmarsaling Record to ShortRecord
type RecordShot struct {
Id string `json:"id"`
Body []string `json:"body"`
Type string `json:"type"`
}
record,_:=json.Marshal(Record)
json.Unmarshal([]byte(record), &RecordShot)
I've got some REST API with my models defined as Go structs.
type User struct {
FirstName string
LastName string
}
Then I've got my database methods for getting data.
GetUserByID(id int) (*User, error)
Now I'd like to replace my REST API with https://github.com/twitchtv/twirp .
Therefore I started defining my models inside .proto files.
message User {
string first_name = 2;
string last_name = 3;
}
Now I've got two User types. Let's call them the native and the proto type.
I've also got a service defined in my .proto file which returns a user to the frontend.
service Users {
rpc GetUser(Id) returns (User);
}
This generates an interface that I have to fill in.
func (s *Server) GetUser(context.Context, id) (*User, error) {
// i'd like to reuse my existing database methods
u, err := db.GetUserByID(id)
// handle error
// do more stuff
return u, nil
}
Unfortunately this does not work. My database returns a native User but the interface requires a proto user.
Is there an easy way to make it work? Maybe using type aliases?
Thanks a lot!
One way you can solve your problem is by doing the conversion manually.
type User struct {
FirstName string
LastName string
}
type protoUser struct {
firstName string
lastName string
}
func main() {
u := db() // Retrieve a user from a mocked db
fmt.Println("Before:")
fmt.Printf("%#v\n", *u) // What db returns (*protoUser)
fmt.Println("After:")
fmt.Printf("%#v\n", u.AsUser()) // What conversion returns (User)
}
// Mocked db that returns pointer to protoUser
func db() *protoUser {
pu := protoUser{"John", "Dough"}
return &pu
}
// Conversion method (converts protoUser into a User)
func (pu *protoUser) AsUser() User {
return User{pu.firstName, pu.lastName}
}
The key part is the AsUser method on the protoUser struct.
There we simply write our custom logic for converting a protoUser into a User type we want to be working with.
Working Example
As #Peter mentioned in the comment section.
I've seen a project which made it with a custom Convert function. It converts the Protobuf to local struct via json.Unmarshal, not sure how's the performance but it's a way to go.
Preview Code PLAYGROUND
// Convert converts the in struct to out struct via `json.Unmarshal`
func Convert(in interface{}, out interface{}) error {
j, err := json.Marshal(in)
if err != nil {
return err
}
err = json.Unmarshal(j, &out)
if err != nil {
return err
}
return nil
}
func main() {
// Converts the protobuf struct to local struct via json.Unmarshal
var localUser User
if err := convert(protoUser, &localUser); err != nil {
panic(err)
}
}
Output
Before:
main.ProtoUser{FirstName:"John", LastName:"Dough"}
After:
main.User{FirstName:"John", LastName:"Dough"}
Program exited.