Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow functions calls in goverter:map #91

Closed
s1berc0de opened this issue Oct 20, 2023 · 19 comments · Fixed by #99
Closed

Allow functions calls in goverter:map #91

s1berc0de opened this issue Oct 20, 2023 · 19 comments · Fixed by #99
Labels
feature New feature or request

Comments

@s1berc0de
Copy link

s1berc0de commented Oct 20, 2023

I think it would be a great idea to map source structure method result to a target structure field (it can help to map private structure fields)

@jmattheis
Copy link
Owner

jmattheis commented Oct 20, 2023

Could you give an example? With goverter comments and your preferred solution.

@s1berc0de
Copy link
Author

s1berc0de commented Oct 20, 2023

Smth like this

type Input struct {
 id uint64
}

func (i Input) ID() uint64 {
 return i.id
}

type Output struct {
 ID uint64
}

// goverter:converter
type Converter interface {
  // goverter:map ID() ID
  Convert(source Input) Output
}

@jmattheis jmattheis changed the title map PrivateField with Method to access it Allow functions calls in goverter:map Oct 22, 2023
@jmattheis
Copy link
Owner

Yeah, sounds good. I'd say goverter should automatically try to map methods to properties, so your example above should be automatically handled. Still it should be possible to manually define it, when the names on the source and target doesn't match

type Input struct {
 id uint64
}

func (i Input) Identifier() uint64 {
 return i.id
}

type Output struct {
 ID uint64
}

// goverter:converter
type Converter interface {
  // goverter:map Identifier() ID
  Convert(source Input) Output
}

@jmattheis jmattheis added the feature New feature or request label Oct 22, 2023
@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

Exactly, method and field can't have the same names, so if goverter can't find struct field with this name, it should to search a method, and now we have one restriction that is one method parameter, I think an idea to convert multi inputs in one output also good, it's pretty good if you use domain aggreagate of few entities

Now goverter can't map fields of anonymous nested struct, dunno why, but it is

@jmattheis
Copy link
Owner

jmattheis commented Oct 22, 2023

now we have one restriction that is one method parameter, I think an idea to convert multi inputs in one output also good, it's pretty good if you use domain aggreagate of few entities

Could you elaborate this with an example? I don't understand what you mean with multiple inputs.

Goverter should be able to map fields on anonymous structs, but the definition is a bit cumbersome, could you give an example that doesn't work?

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

About multi inputs:
type EntityMixin struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
ID uint64
}

type Account struct {
Version time.Time
ConfirmedAt sql.NullTime
Email string
Password string
EntityMixin
Status enums.AccountStatus
}

type Role struct {
ID uint64
Type enums.RoleType
}

type Group struct {
gorm.EntityMixin
Name string
}

// goverter:converter
type Converter interface {
// goverter:map accountEntity.EntityMixin.ID ID
// goverter:map accountEntity.Email Email
// goverter:map accountEntity.Version Version
// goverter:map roleEntity.Type RoleType
// goverter:map groupEntity.EntityMixin.ID GroupID
DatabaseToEntities(accountEntity *entities.Account, roleEntity *entities.Role, groupEntity *entities.Group) *identity.Identity
}

Error while creating converter method: ...

expected signature to have only one parameter
exit status 1

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

About one input, but nested EntityMixin struct, entities the same as upper, but contract is:

type DatabaseToEntitiesInput struct {
	AccountEntity *entities.Account
	RoleEntity    *entities.Role
	GroupEntity   *entities.Group
}

// goverter:converter
type Converter interface {
	// goverter:map AccountEntity.EntityMixin.ID ID
	// goverter:map AccountEntity.Email              Email
	// goverter:map AccountEntity.Version           Version
	// goverter:map RoleEntity.Type                     RoleType
	// goverter:map GroupEntity.EntityMixin.ID    GroupID
	DatabaseToEntities() *identity.Identity
}

Error while creating converter method: ....

| |
| |
| |
source*.???
target*.ID
| | |
| | | uint64
| |
...

Cannot match the target field with the source entry: "ID" does not exist.
exit status 1

Can't find AccountEntity.EntityMixin.ID source, EntityMixin is anonimus nested struct

@jmattheis
Copy link
Owner

jmattheis commented Oct 22, 2023

Multiple inputs will probably be supported with #68 I'll keep this in mind when implementing it.

Do you mean in the second example DatabaseToEntities(*DatabaseToEntitiesInput) *identity.Identity?

If yes, then you can use autoMap for this

// goverter:converter
type Converter interface {
	// goverter:autoMap AccountEntity
	// goverter:autoMap RoleEntity
	// goverter:autoMap GroupEntity
	DatabaseToEntities(*DatabaseToEntitiesInput) *identity.Identity
}

See https://goverter.jmattheis.de/#/conversion/mapping?id=auto-map & #55

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

autoMap doesn't work because of anonymous nested EntityMixin, still error: Cannot match the target field with the source entry: "ID" does not exist.
I see it works nice if all fields have names, but here is

type EntityMixin struct {
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
ID uint64
}

type Account struct {
Version time.Time
ConfirmedAt sql.NullTime
Email string
Password string
EntityMixin
Status enums.AccountStatus
}

@jmattheis
Copy link
Owner

Can you provide a smaller example that is self contained? You should be able to access the anonymous or rather embedded struct by the type name in your case EntityMixin in goverter. See #24

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

Yes, if i write the mapping function i can get access to the ID field through the EntityMixin, but goverter doesn't see this ability

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

type A struct {
	ID uint64
}

type B struct {
	A
	Test string
}

type C struct {
	ID   uint64
	Test string
}

type DatabaseToEntitiesInput struct {
	B B
}

// goverter:converter
type Converter interface {
	// goverter:autoMap B
	DatabaseToEntities(*DatabaseToEntitiesInput) *C
}

Gets error too

@s1berc0de
Copy link
Author

Oh, if i don't use pointers inside of DatabaseToEntitiesInput it can map field from embedded struct

@jmattheis
Copy link
Owner

ID is on the A struct so it isn't seen by just automapping B, you need properties from both B and B.A:

// goverter:converter
type Converter interface {
	// goverter:autoMap B.A
	// goverter:autoMap B
	DatabaseToEntities(*DatabaseToEntitiesInput) *C
}

This builds without problems.

@s1berc0de
Copy link
Author

Yeah, got now problems with mapping time.Time type

| | |
| | | | time.Time
| | | |
| | | |
| | | |
source*.AccountEntity.Version.???
target* .Version.wall
| | | |
| | | | uint64
| | |
| | | time.Time
| |
| |
| |

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

type A struct {
ID uint64
}

type B struct {
A
Version time.Time
Test string
}

type C struct {
ID uint64
Version time.Time
Test string
}

type DatabaseToEntitiesInput struct {
B B
}

// goverter:converter
type Converter interface {
// goverter:map B.A.ID ID
// goverter:map B.Test Test
// goverter:map B.Version Version
DatabaseToEntities(*DatabaseToEntitiesInput) *C
}

goverter tries to watch inside of time.Time structure instead of copy as object

@jmattheis
Copy link
Owner

Goverter doesn't know that see #4

@s1berc0de
Copy link
Author

s1berc0de commented Oct 22, 2023

Yeah i see, thnq for the discussion, so now we have 2 features: #68 and that is in the title

@jmattheis
Copy link
Owner

Feature added in v1.1.0. Example:

Input:

package structs

// goverter:converter
type Converter interface {
    Convert(source Input) Output
}

type Input struct {
    Name string
}
func (Input) Age() int {
    return 42
}
type Output struct {
    Name string
    Age int
}

Output:

// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.

package generated

import execution "github.com/jmattheis/goverter/execution"

type ConverterImpl struct{}

func (c *ConverterImpl) Convert(source execution.Input) execution.Output {
    var structsOutput execution.Output
    structsOutput.Name = source.Name
    structsOutput.Age = source.Age()
    return structsOutput
}

Fixes #91

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants