Écrivons du code python qui analyse le code go et génère du code go

Cet article est l'article du 14ème jour du Calendrier de l'Avent WACUL 2016. La personne qui écrit travaille chez WACUL depuis septembre de cette année, principalement en écrivant du code go sur le back-end et parfois en python.

Cet article est une continuation de l'article précédent (http://qiita.com/podhmo/items/a12952b07648e42911b4) (l'article précédent est inutilement long donc vous n'avez pas à le forcer à le lire).

La dernière fois, j'ai écrit du code python qui génère du code go à partir d'un exemple de réponse API en imitant JSON-to-Go. .. Cette fois, je voudrais générer du code go en utilisant le résultat de l'analyse du code go.

introduction

Génération automatique du processus de conversion

Par exemple, l'utilisation de go-swagger générera une définition de structure pour la réponse. Il s'agit d'une structure avec une structure similaire, bien qu'elle soit légèrement différente de la structure stockée dans la base de données interne.

Lors de la création de l'API Web réelle, il peut être nécessaire de convertir la structure qui est conservée dans la base de données en structure pour cette réponse. C'est plutôt ennuyeux et fastidieux. Je souhaite générer automatiquement le code de cette partie.

Aperçu

Pour résumer un peu plus l'histoire, ce que je veux, c'est le code de conversion du modèle (src) en objet de réponse (dst). Pour obtenir cela, analysez la définition de structure de la source de conversion et de la destination de conversion et extrayez les informations de type. Dans ce domaine, go est fait avec un package ast etc. Après avoir extrait les informations de type avec l'outil créé, elles sont sorties au format JSON. Il s'agit d'une tentative de générer un code de conversion de src vers dst en utilisant ce JSON.

sample: github api

Je voudrais présenter un exemple d'exécution de la génération de code à l'aide d'un exemple textuel.

À l'origine, c'est une histoire pour générer un processus pour convertir l'expression de la couche de persistance en expression de réponse, mais c'est gênant, donc c'est un peu arbitraire, mais la génération automatique du code du processus de conversion se fait sous la forme suivante. Je voudrais l'essayer.

  1. Générez une structure à partir de l'exemple de réponse d'une belle API (Utilisez le code créé dans l'article précédent)
  2. Générez une structure à partir de yaml d'un joli swagger (en utilisant go-swagger)

Je voudrais générer automatiquement le processus de conversion de src en dst, en supposant que le code go des deux structures générées par celles-ci est la structure de l'expression de réponse de model et api pour la persistance, respectivement.

La génération automatique elle-même est créée par la procédure suivante.

  1. Extrayez les informations de la définition de structure de la source go ʻextract :: go [src] => JSON [src] `
  2. Extrayez les informations de la définition struct de la destination go ʻextract :: go [dst] => JSON [dst] `
  3. Générez du code go à partir des informations extraites convert :: JSON [src], json [dst] => go [convert]

À l'aide de ces deux éléments, le code du processus de conversion de la structure source de conversion (src) vers la structure de destination de conversion (dst) est généré.

Générer du code go (src)

Prenons l'exemple de réponse de l'api pour obtenir l'utilisateur authentifié de la partie suivante du document de github. Il n'y a aucune raison particulière de choisir cette API. C'est tout parce que je l'ai remarqué d'une manière ou d'une autre.

Enregistrez-le sous github-get-authenticated-user.json.

{
  "login": "octocat",
  "id": 1,
  "avatar_url": "https://github.com/images/error/octocat_happy.gif",
  "gravatar_id": "",
  "url": "https://api.github.com/users/octocat",
  "html_url": "https://github.com/octocat",
  "followers_url": "https://api.github.com/users/octocat/followers",
  "following_url": "https://api.github.com/users/octocat/following{/other_user}",
  "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
  "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
  "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
  "organizations_url": "https://api.github.com/users/octocat/orgs",
  "repos_url": "https://api.github.com/users/octocat/repos",
  "events_url": "https://api.github.com/users/octocat/events{/privacy}",
  "received_events_url": "https://api.github.com/users/octocat/received_events",
  "type": "User",
  "site_admin": false,
  "name": "monalisa octocat",
  "company": "GitHub",
  "blog": "https://github.com/blog",
  "location": "San Francisco",
  "email": "[email protected]",
  "hireable": false,
  "bio": "There once was...",
  "public_repos": 2,
  "public_gists": 1,
  "followers": 20,
  "following": 0,
  "created_at": "2008-01-14T04:33:35Z",
  "updated_at": "2008-01-14T04:33:35Z",
  "total_private_repos": 100,
  "owned_private_repos": 100,
  "private_gists": 81,
  "disk_usage": 10000,
  "collaborators": 8,
  "plan": {
    "name": "Medium",
    "space": 400,
    "private_repos": 20,
    "collaborators": 0
  }
}

Générez la définition de structure à partir d'ici. Utilisez Script créé dans l'article précédent.

$ swagger_src
mkdir -p src/github
python src/jsontogo/jsontogo.py json/github-get-authenticated-user.json --package github --name AuthenticatedUser | gofmt > src/github/authenticated_user.go

Vous pouvez obtenir la définition de structure suivante. (Enregistrer sous src / github / authenticated_user.go)

package github

import (
	"time"
)

/* structure
AuthenticatedUser
	Plan
*/
// AuthenticatedUser : auto generated JSON container
type AuthenticatedUser struct {
	AvatarURL         string    `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
	Bio               string    `json:"bio" example:"There once was..."`
	Blog              string    `json:"blog" example:"https://github.com/blog"`
	Collaborators     int       `json:"collaborators" example:"8"`
	Company           string    `json:"company" example:"GitHub"`
	CreatedAt         time.Time `json:"created_at" example:"2008-01-14T04:33:35Z"`
	DiskUsage         int       `json:"disk_usage" example:"10000"`
	Email             string    `json:"email" example:"[email protected]"`
	EventsURL         string    `json:"events_url" example:"https://api.github.com/users/octocat/events{/privacy}"`
	Followers         int       `json:"followers" example:"20"`
	FollowersURL      string    `json:"followers_url" example:"https://api.github.com/users/octocat/followers"`
	Following         int       `json:"following" example:"0"`
	FollowingURL      string    `json:"following_url" example:"https://api.github.com/users/octocat/following{/other_user}"`
	GistsURL          string    `json:"gists_url" example:"https://api.github.com/users/octocat/gists{/gist_id}"`
	GravatarID        string    `json:"gravatar_id" example:""`
	HTMLURL           string    `json:"html_url" example:"https://github.com/octocat"`
	Hireable          bool      `json:"hireable" example:"False"`
	ID                int       `json:"id" example:"1"`
	Location          string    `json:"location" example:"San Francisco"`
	Login             string    `json:"login" example:"octocat"`
	Name              string    `json:"name" example:"monalisa octocat"`
	OrganizationsURL  string    `json:"organizations_url" example:"https://api.github.com/users/octocat/orgs"`
	OwnedPrivateRepos int       `json:"owned_private_repos" example:"100"`
	Plan              Plan      `json:"plan"`
	PrivateGists      int       `json:"private_gists" example:"81"`
	PublicGists       int       `json:"public_gists" example:"1"`
	PublicRepos       int       `json:"public_repos" example:"2"`
	ReceivedEventsURL string    `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
	ReposURL          string    `json:"repos_url" example:"https://api.github.com/users/octocat/repos"`
	SiteAdmin         bool      `json:"site_admin" example:"False"`
	StarredURL        string    `json:"starred_url" example:"https://api.github.com/users/octocat/starred{/owner}{/repo}"`
	SubscriptionsURL  string    `json:"subscriptions_url" example:"https://api.github.com/users/octocat/subscriptions"`
	TotalPrivateRepos int       `json:"total_private_repos" example:"100"`
	Type              string    `json:"type" example:"User"`
	URL               string    `json:"url" example:"https://api.github.com/users/octocat"`
	UpdatedAt         time.Time `json:"updated_at" example:"2008-01-14T04:33:35Z"`
}

// Plan : auto generated JSON container
type Plan struct {
	Collaborators int    `json:"collaborators" example:"0"`
	Name          string `json:"name" example:"Medium"`
	PrivateRepos  int    `json:"private_repos" example:"20"`
	Space         int    `json:"space" example:"400"`
}

Vous avez maintenant la définition de structure pour la source (src).

Générer du code go (dst)

Ensuite, récupérons la définition de structure pour la destination de conversion (dst). Obtenez la définition de structure go en utilisant go-swagger à partir de la définition swagger.

api.gru

Il existe un service appelé api.gru. Il s'agit d'un service qui partage les spécifications API des services Web. Beaucoup sont fournis au format OpenAPI / Swagger 2.0. Au fait, si vous regardez swagger.yaml que vous pouvez obtenir ici en utilisant ReDoc etc., vous pouvez comprendre à quoi ressemble swagger.

api.gru

Essayons la génération automatique en utilisant l'API de github comme exemple. Téléchargez le yaml de la définition swagger à partir de l'URL suivante.

$ make swagger_fetch
mkdir -p yaml
wget https://api.apis.guru/v2/specs/github.com/v3/swagger.yaml -O yaml/github-swagger.yaml
gsed -i "s/type: *'null'/type: object/g; s/'+1':/'plus1':/g; s/'-1':/'minus1':/" yaml/github-swagger.yaml

Vous pouvez obtenir yaml comme le lien. Le sed après wget est une correction mineure pour résoudre le problème actuel de go-swagger et le problème enregistré de swagger.yaml.

Extraction des informations de type à partir de la définition de struct

こちらは普通にgo-swaggerを実行するだけです。以下の様にして実行します。今回はstructの変換だけなのでmodelのみを生成します。本筋とは関係ないのですがなぜか"github.com/go-openapi/swag"のimportが出力されないという問題があったためgoimportsで追加しています。

$ make swagger_gen
rm -rf dst/swagger/gen
mkdir -p dst/swagger/gen
swagger generate model -f yaml/github-swagger.yaml --target dst/swagger/gen --model-package def
goimports -w dst/swagger/gen/def/*.go

Beaucoup de code est généré, mais cette fois, seul User.go s'en soucie.

package def

// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command

import (
	strfmt "github.com/go-openapi/strfmt"
	"github.com/go-openapi/swag"

	"github.com/go-openapi/errors"
)

// User user
// swagger:model user
type User struct {

	// avatar url
	AvatarURL string `json:"avatar_url,omitempty"`

	// bio
	Bio string `json:"bio,omitempty"`

	// blog
	Blog string `json:"blog,omitempty"`

	// collaborators
	Collaborators int64 `json:"collaborators,omitempty"`

	// company
	Company string `json:"company,omitempty"`

	// ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
	CreatedAt string `json:"created_at,omitempty"`

	// disk usage
	DiskUsage int64 `json:"disk_usage,omitempty"`

	// email
	Email string `json:"email,omitempty"`

	// followers
	Followers int64 `json:"followers,omitempty"`

	// following
	Following int64 `json:"following,omitempty"`

	// gravatar id
	GravatarID string `json:"gravatar_id,omitempty"`

	// hireable
	Hireable bool `json:"hireable,omitempty"`

	// html url
	HTMLURL string `json:"html_url,omitempty"`

	// id
	ID int64 `json:"id,omitempty"`

	// location
	Location string `json:"location,omitempty"`

	// login
	Login string `json:"login,omitempty"`

	// name
	Name string `json:"name,omitempty"`

	// owned private repos
	OwnedPrivateRepos int64 `json:"owned_private_repos,omitempty"`

	// plan
	Plan *UserPlan `json:"plan,omitempty"`

	// private gists
	PrivateGists int64 `json:"private_gists,omitempty"`

	// public gists
	PublicGists int64 `json:"public_gists,omitempty"`

	// public repos
	PublicRepos int64 `json:"public_repos,omitempty"`

	// total private repos
	TotalPrivateRepos int64 `json:"total_private_repos,omitempty"`

	// type
	Type string `json:"type,omitempty"`

	// url
	URL string `json:"url,omitempty"`
}

// Validate validates this user
func (m *User) Validate(formats strfmt.Registry) error {
	var res []error

	if err := m.validatePlan(formats); err != nil {
		// prop
		res = append(res, err)
	}

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}

func (m *User) validatePlan(formats strfmt.Registry) error {

	if swag.IsZero(m.Plan) { // not required
		return nil
	}

	if m.Plan != nil {

		if err := m.Plan.Validate(formats); err != nil {
			return err
		}
	}

	return nil
}

// UserPlan user plan
// swagger:model UserPlan
type UserPlan struct {

	// collaborators
	Collaborators int64 `json:"collaborators,omitempty"`

	// name
	Name string `json:"name,omitempty"`

	// private repos
	PrivateRepos int64 `json:"private_repos,omitempty"`

	// space
	Space int64 `json:"space,omitempty"`
}

// Validate validates this user plan
func (m *UserPlan) Validate(formats strfmt.Registry) error {
	var res []error

	if len(res) > 0 {
		return errors.CompositeValidationError(res...)
	}
	return nil
}

Extraction des informations de type de struct

Au moins, vous devez extraire les informations de type du code go dans la définition de structure. Faisons de notre mieux dans ce domaine. Combien si vous avez un esprit avec ast. J'ai créé une commande appelée go-structjson. Ne vous attendez pas à trop de qualité de code à l'intérieur car c'est un désordre.

À partir du code comme ci-dessous

package alias

// Person :
type Person struct {
	Name string
}

type P *Person
type PS []Person
type PS2 []P
type PSP *[]P

Génère le JSON suivant.

{
  "module": {
    "alias": {
      "file": {
        "GOPATH/src/github.com/podhmo/go-structjson/examples/alias/person.go": {
          "alias": {
            "P": {
              "candidates": null,
              "name": "P",
              "original": {
                "kind": "pointer",
                "value": {
                  "kind": "primitive",
                  "value": "Person"
                }
              }
            },
            "PS": {
              "candidates": null,
              "name": "PS",
              "original": {
                "kind": "array",
                "value": {
                  "kind": "primitive",
                  "value": "Person"
                }
              }
            },
            "PS2": {
              "candidates": null,
              "name": "PS2",
              "original": {
                "kind": "array",
                "value": {
                  "kind": "primitive",
                  "value": "P"
                }
              }
            },
            "PSP": {
              "candidates": null,
              "name": "PSP",
              "original": {
                "kind": "pointer",
                "value": {
                  "kind": "array",
                  "value": {
                    "kind": "primitive",
                    "value": "P"
                  }
                }
              }
            }
          },
          "name": "GOPATH/src/github.com/podhmo/go-structjson/examples/alias/person.go",
          "struct": {
            "Person": {
              "fields": {
                "Name": {
                  "embed": false,
                  "name": "Name",
                  "tags": {},
                  "type": {
                    "kind": "primitive",
                    "value": "string"
                  }
                }
              },
              "name": "Person"
            }
          }
        }
      },
      "fullname": "github.com/podhmo/go-structjson/examples/alias",
      "name": "alias"
    }
  }
}

Il y avait une histoire que le nom newtype est plus approprié pour ceux gérés par l'alias de nom. Ici, type MyInt int etc. est appelé alias de type. Pardon.

Un fichier JSON est généré pour chaque src et dst.

$ make swagger_extract
mkdir -p dst/swagger/convert
mkdir -p json/extracted
go-structjson -target src/github > json/extracted/src.json
go-structjson -target dst/swagger/gen/def > json/extracted/dst.json

Génération de code pour la conversion à partir du JSON extrait des informations de type

Eh bien, j'ai pu obtenir le code de la structure de la source de conversion et de la destination de la conversion. Vient ensuite la génération automatique du traitement de conversion. Je crée une bibliothèque appelée goconvert pour cette fois. Le fait qu'il se fasse apparaît à l'endroit où il n'y a qu'à faire est écrit dans le read me. Je vais l'utiliser pour le moment. Code de conversion de src en dst à l'aide de convert.py effectivement créé à l'aide de cette bibliothèque Générer.

$make swagger_convert
mkdir -p dst/swagger/convert
python src/convert.py --logger=DEBUG --src json/extracted/src.json --dst json/extracted/dst.json --override json/extracted/convert.json > dst/swagger/convert/autogen.go || rm dst/swagger/convert/autogen.go
INFO:goconvert.builders:start register function AuthenticatedUserToUser: ['github.com/podhmo/advent2016/src/github.AuthenticatedUser'] -> ['github.com/podhmo/advent2016/dst/swagger/gen/def.User']
DEBUG:goconvert.builders:resolve: avatarurl ('string',) -> avatarurl ('string',)
DEBUG:goconvert.minicode:gencode: ('string',) -> ('string',)
...
NotImplementedError: mapping not found ('time.Time',) -> ('string',)

Oh. Ça ne marche pas.

Je n'arrive pas à générer le code pour string from time.Time. En regardant à l'intérieur, il me semblait que j'avais du mal à gérer Created At.

src (généré à partir de la réponse de l'API)

type AuthenticatedUser struct {
	AvatarURL         string    `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
...
	CreatedAt         time.Time `json:"created_at" example:"2008-01-14T04:33:35Z"`

dst (généré à partir de swagger.yaml)

// User user
// swagger:model user
type User struct {

	// avatar url
	AvatarURL string `json:"avatar_url,omitempty"`

...
	// ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ
	CreatedAt string `json:"created_at,omitempty"`

Eh bien, c'est en un sens naturel et inévitable. Peu importe combien vous tracez les informations de type go, vous ne savez pas comment convertir time.Time en chaîne. Seul le développeur le sait. Écrivez le processus de conversion time.Time en chaîne comme primitive.go dans le même package que la destination de sortie du processus de conversion.

package convert

import "time"

// TimeToString :
func TimeToString(t time.Time) string {
	return t.Format(time.RFC3339)
}

Puis refaites la sortie JSON. Cette fois, go-funcjson est également ajouté (en passant le JSON généré par go-funcjson, la fonction de traitement de conversion manuscrite sera utilisée telle quelle au moment de la génération automatique).

$ make swagger_extract
mkdir -p dst/swagger/convert
mkdir -p json/extracted
go-structjson -target src/github > json/extracted/src.json
go-structjson -target dst/swagger/gen/def > json/extracted/dst.json
go-funcjson -target dst/swagger/convert > json/extracted/convert.json || echo '{}' > json/extracted/convert.json
...
-- write: convert --
gofmt -w dst/swagger/convert/autogen.go

Cette fois, l'opération a réussi. Le code suivant est généré (dst / swagger / convert / autogen.go).

package convert

import (
	def "github.com/podhmo/advent2016/dst/swagger/gen/def"
	github "github.com/podhmo/advent2016/src/github"
)

// AuthenticatedUserToUser :
func AuthenticatedUserToUser(from *github.AuthenticatedUser) *def.User {
	to := &def.User{}
	to.AvatarURL = from.AvatarURL
	to.Bio = from.Bio
	to.Blog = from.Blog
	tmp1 := (int64)(from.Collaborators)
	to.Collaborators = tmp1
	to.Company = from.Company
	to.CreatedAt = TimeToString(from.CreatedAt)
	tmp2 := (int64)(from.DiskUsage)
	to.DiskUsage = tmp2
	to.Email = from.Email
	tmp3 := (int64)(from.Followers)
	to.Followers = tmp3
	tmp4 := (int64)(from.Following)
	to.Following = tmp4
	to.GravatarID = from.GravatarID
	to.Hireable = from.Hireable
	to.HTMLURL = from.HTMLURL
	tmp5 := (int64)(from.ID)
	to.ID = tmp5
	to.Location = from.Location
	to.Login = from.Login
	to.Name = from.Name
	tmp6 := (int64)(from.OwnedPrivateRepos)
	to.OwnedPrivateRepos = tmp6
	to.Plan = PlanToUserPlan(&(from.Plan))
	tmp7 := (int64)(from.PrivateGists)
	to.PrivateGists = tmp7
	tmp8 := (int64)(from.PublicGists)
	to.PublicGists = tmp8
	tmp9 := (int64)(from.PublicRepos)
	to.PublicRepos = tmp9
	tmp10 := (int64)(from.TotalPrivateRepos)
	to.TotalPrivateRepos = tmp10
	to.Type = from.Type
	to.URL = from.URL
	return to
}

// PlanToUserPlan :
func PlanToUserPlan(from *github.Plan) *def.UserPlan {
	to := &def.UserPlan{}
	tmp11 := (int64)(from.Collaborators)
	to.Collaborators = tmp11
	to.Name = from.Name
	tmp12 := (int64)(from.PrivateRepos)
	to.PrivateRepos = tmp12
	tmp13 := (int64)(from.Space)
	to.Space = tmp13
	return to
}

Le TimeToString () ajouté dans primitive.go est également utilisé dans la partie CreatedAt. Va-t-il compiler?

$ (cd dst/swagger/convert/; go install)

Ça a l'air bien.

Exécuter réellement le processus de conversion

Utilisons en fait le code de traitement de conversion généré. Exécutez le code suivant.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/davecgh/go-spew/spew"
	"github.com/podhmo/advent2016/dst/swagger/convert"
	"github.com/podhmo/advent2016/src/github"
)

func main() {
	decoder := json.NewDecoder(os.Stdin)
	var from github.AuthenticatedUser
	if err := decoder.Decode(&from); err != nil {
		log.Fatal(err)
	}

	{
		fmt.Println("------------------------------")
		fmt.Println("source:")
		fmt.Println("------------------------------")
		spew.Dump(from)
	}
	{
		to := convert.AuthenticatedUserToUser(&from)
		fmt.Println("------------------------------")
		fmt.Println("destination:")
		fmt.Println("------------------------------")
		spew.Dump(to)
	}
}

Si le code de traitement de conversion est généré avec succès, la compilation réussira. Vous devriez pouvoir le faire. Comme c'est gênant, passons la réponse de l'API github téléchargée à l'origine à l'entrée.

$ make swagger_run1
cat json/github-get-authenticated-user.json | go run dst/swagger/main/spew/*.go
------------------------------
source:
------------------------------
(github.AuthenticatedUser) {
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC,
 DiskUsage: (int) 10000,
 Email: (string) (len=18) "[email protected]",
 EventsURL: (string) (len=53) "https://api.github.com/users/octocat/events{/privacy}",
 Followers: (int) 20,
 FollowersURL: (string) (len=46) "https://api.github.com/users/octocat/followers",
 Following: (int) 0,
 FollowingURL: (string) (len=59) "https://api.github.com/users/octocat/following{/other_user}",
 GistsURL: (string) (len=52) "https://api.github.com/users/octocat/gists{/gist_id}",
 GravatarID: (string) "",
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 Hireable: (bool) false,
 ID: (int) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OrganizationsURL: (string) (len=41) "https://api.github.com/users/octocat/orgs",
 OwnedPrivateRepos: (int) 100,
 Plan: (github.Plan) {
  Collaborators: (int) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int) 20,
  Space: (int) 400
 },
 PrivateGists: (int) 81,
 PublicGists: (int) 1,
 PublicRepos: (int) 2,
 ReceivedEventsURL: (string) (len=52) "https://api.github.com/users/octocat/received_events",
 ReposURL: (string) (len=42) "https://api.github.com/users/octocat/repos",
 SiteAdmin: (bool) false,
 StarredURL: (string) (len=59) "https://api.github.com/users/octocat/starred{/owner}{/repo}",
 SubscriptionsURL: (string) (len=50) "https://api.github.com/users/octocat/subscriptions",
 TotalPrivateRepos: (int) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat",
 UpdatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC
}
------------------------------
destination:
------------------------------
(*def.User)(0xc4200ae140)({
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int64) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (string) (len=20) "2008-01-14T04:33:35Z",
 DiskUsage: (int64) 10000,
 Email: (string) (len=18) "[email protected]",
 Followers: (int64) 20,
 Following: (int64) 0,
 GravatarID: (string) "",
 Hireable: (bool) false,
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 ID: (int64) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OwnedPrivateRepos: (int64) 100,
 Plan: (*def.UserPlan)(0xc4204da1b0)({
  Collaborators: (int64) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int64) 20,
  Space: (int64) 400
 }),
 PrivateGists: (int64) 81,
 PublicGists: (int64) 1,
 PublicRepos: (int64) 2,
 TotalPrivateRepos: (int64) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat"
})

Cela semble fonctionner. (Au fait, si la structure est exactement la même, il peut être plus facile de convertir la valeur via JSON avec json.Unmarshal après json.Marshal)

Le Makefile pour les commandes utilisées pour la conversion est ici.

Parlez plus loin lorsque le type existant est mis à jour

En fait, le src du code utilisé dans la conversion de code jusqu'à présent est le même que celui de l'article précédent, mais ce n'est pas complètement le même. Dernier code Je commentais la partie suivante.

        # elif "://" in val:
        #     return "github.com/go-openapi/strfmt.Uri"

Décommentez ceci et essayez de le générer à nouveau.

$ make swagger_src
...
$ make swagger_extract
...
$ make swagger_convert
...
KeyError: 'Uri'

J'ai échoué. En fait, c'est parce que la bibliothèque github.com/go-openapi/strfmt que j'utilise depuis que j'ai écrit l'article précédent a été mise à jour. fait. Plus précisément, il semble que «strfmt.Uri» soit devenu «strfmt.URI».

Mettons-le à jour à nouveau et générons-le à nouveau.

$ go get -u github.com/go-openapi/strfmt
$ gsed -i 's/strfmt.Uri/strfmt.URI/' src/jsontogo/jsontogo.py
$ make swagger_src
$ make swagger_extract
$ make swagger_convert

Cette fois, il semble avoir réussi. Bien sûr, vous pouvez également effectuer des conversions.

$ make swagger_run1
cat json/github-get-authenticated-user.json | go run dst/swagger/main/spew/*.go
------------------------------
source:
------------------------------
(github.AuthenticatedUser) {
 AvatarURL: (strfmt.URI) (len=49) https://github.com/images/error/octocat_happy.gif,
 Bio: (string) (len=17) "There once was...",
 Blog: (strfmt.URI) (len=23) https://github.com/blog,
 Collaborators: (int) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC,
 DiskUsage: (int) 10000,
 Email: (string) (len=18) "[email protected]",
 EventsURL: (strfmt.URI) (len=53) https://api.github.com/users/octocat/events{/privacy},
 Followers: (int) 20,
 FollowersURL: (strfmt.URI) (len=46) https://api.github.com/users/octocat/followers,
 Following: (int) 0,
 FollowingURL: (strfmt.URI) (len=59) https://api.github.com/users/octocat/following{/other_user},
 GistsURL: (strfmt.URI) (len=52) https://api.github.com/users/octocat/gists{/gist_id},
 GravatarID: (string) "",
 HTMLURL: (strfmt.URI) (len=26) https://github.com/octocat,
 Hireable: (bool) false,
 ID: (int) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OrganizationsURL: (strfmt.URI) (len=41) https://api.github.com/users/octocat/orgs,
 OwnedPrivateRepos: (int) 100,
 Plan: (github.Plan) {
  Collaborators: (int) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int) 20,
  Space: (int) 400
 },
 PrivateGists: (int) 81,
 PublicGists: (int) 1,
 PublicRepos: (int) 2,
 ReceivedEventsURL: (strfmt.URI) (len=52) https://api.github.com/users/octocat/received_events,
 ReposURL: (strfmt.URI) (len=42) https://api.github.com/users/octocat/repos,
 SiteAdmin: (bool) false,
 StarredURL: (strfmt.URI) (len=59) https://api.github.com/users/octocat/starred{/owner}{/repo},
 SubscriptionsURL: (strfmt.URI) (len=50) https://api.github.com/users/octocat/subscriptions,
 TotalPrivateRepos: (int) 100,
 Type: (string) (len=4) "User",
 URL: (strfmt.URI) (len=36) https://api.github.com/users/octocat,
 UpdatedAt: (time.Time) 2008-01-14 04:33:35 +0000 UTC
}
------------------------------
destination:
------------------------------
(*def.User)(0xc42015e280)({
 AvatarURL: (string) (len=49) "https://github.com/images/error/octocat_happy.gif",
 Bio: (string) (len=17) "There once was...",
 Blog: (string) (len=23) "https://github.com/blog",
 Collaborators: (int64) 8,
 Company: (string) (len=6) "GitHub",
 CreatedAt: (string) (len=20) "2008-01-14T04:33:35Z",
 DiskUsage: (int64) 10000,
 Email: (string) (len=18) "[email protected]",
 Followers: (int64) 20,
 Following: (int64) 0,
 GravatarID: (string) "",
 Hireable: (bool) false,
 HTMLURL: (string) (len=26) "https://github.com/octocat",
 ID: (int64) 1,
 Location: (string) (len=13) "San Francisco",
 Login: (string) (len=7) "octocat",
 Name: (string) (len=16) "monalisa octocat",
 OwnedPrivateRepos: (int64) 100,
 Plan: (*def.UserPlan)(0xc420330420)({
  Collaborators: (int64) 0,
  Name: (string) (len=6) "Medium",
  PrivateRepos: (int64) 20,
  Space: (int64) 400
 }),
 PrivateGists: (int64) 81,
 PublicGists: (int64) 1,
 PublicRepos: (int64) 2,
 TotalPrivateRepos: (int64) 100,
 Type: (string) (len=4) "User",
 URL: (string) (len=36) "https://api.github.com/users/octocat"
})

en conclusion

Cette fois, j'ai analysé le code de go et généré le code de conversion d'une structure à une autre. Au fait, c'était gênant, alors j'ai arrêté d'écrire des histoires internes détaillées. J'ai utilisé python pour la génération de code, mais il peut être plus beau et plus pratique à compléter avec go. Il y a quelques sujets dont je peux parler, comme la partie analyse du pointeur, la gestion des alias (nouveau type), le code correspondant aux tranches, que faire si la correspondance de conversion n'est pas trouvée, donc je peux l'écrire si j'en ai envie Peut être.

prime

J'ajouterai un bonus car le code généré n'a pas d'élan. J'essaierai d'être méchant, comme jouer avec certaines définitions de type de src pour en faire un pointeur.

diff --git a/src/github/authenticated_user.go b/src/github/authenticated_user.go
index 8893144..7e04142 100644
--- a/src/github/authenticated_user.go
+++ b/src/github/authenticated_user.go
@@ -11,8 +11,8 @@ AuthenticatedUser
 */
 // AuthenticatedUser : auto generated JSON container
 type AuthenticatedUser struct {
-	AvatarURL         strfmt.URI `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
-	Bio               string     `json:"bio" example:"There once was..."`
+	AvatarURL         *******strfmt.URI `json:"avatar_url" example:"https://github.com/images/error/octocat_happy.gif"`
+	Bio               *********string     `json:"bio" example:"There once was..."`
 	Blog              strfmt.URI `json:"blog" example:"https://github.com/blog"`
 	Collaborators     int        `json:"collaborators" example:"8"`
 	Company           string     `json:"company" example:"GitHub"`
@@ -35,10 +35,10 @@ type AuthenticatedUser struct {
 	OrganizationsURL  strfmt.URI `json:"organizations_url" example:"https://api.github.com/users/octocat/orgs"`
 	OwnedPrivateRepos int        `json:"owned_private_repos" example:"100"`
 	Plan              Plan       `json:"plan"`
-	PrivateGists      int        `json:"private_gists" example:"81"`
-	PublicGists       int        `json:"public_gists" example:"1"`
-	PublicRepos       int        `json:"public_repos" example:"2"`
-	ReceivedEventsURL strfmt.URI `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
+	PrivateGists      *int        `json:"private_gists" example:"81"`
+	PublicGists       **int        `json:"public_gists" example:"1"`
+	PublicRepos       ***int        `json:"public_repos" example:"2"`
+	ReceivedEventsURL ****strfmt.URI `json:"received_events_url" example:"https://api.github.com/users/octocat/received_events"`
 	ReposURL          strfmt.URI `json:"repos_url" example:"https://api.github.com/users/octocat/repos"`
 	SiteAdmin         bool       `json:"site_admin" example:"False"`
 	StarredURL        strfmt.URI `json:"starred_url" example:"https://api.github.com/users/octocat/starred{/owner}{/repo}"`

Le code généré ressemble à ceci: Cela semble fonctionner correctement. À bien y penser, il n'y avait pas de tranches dans cet exemple.

package convert

import (
	def "github.com/podhmo/advent2016/dst/swagger/gen/def"
	github "github.com/podhmo/advent2016/src/github"
)

// AuthenticatedUserToUser :
func AuthenticatedUserToUser(from *github.AuthenticatedUser) *def.User {
	to := &def.User{}
	if from.AvatarURL != nil {
		tmp1 := *(from.AvatarURL)
		if tmp1 != nil {
			tmp2 := *(tmp1)
			if tmp2 != nil {
				tmp3 := *(tmp2)
				if tmp3 != nil {
					tmp4 := *(tmp3)
					if tmp4 != nil {
						tmp5 := *(tmp4)
						if tmp5 != nil {
							tmp6 := *(tmp5)
							if tmp6 != nil {
								tmp7 := *(tmp6)
								tmp8 := (string)(tmp7)
								to.AvatarURL = tmp8
							}
						}
					}
				}
			}
		}
	}
	if from.Bio != nil {
		tmp9 := *(from.Bio)
		if tmp9 != nil {
			tmp10 := *(tmp9)
			if tmp10 != nil {
				tmp11 := *(tmp10)
				if tmp11 != nil {
					tmp12 := *(tmp11)
					if tmp12 != nil {
						tmp13 := *(tmp12)
						if tmp13 != nil {
							tmp14 := *(tmp13)
							if tmp14 != nil {
								tmp15 := *(tmp14)
								if tmp15 != nil {
									tmp16 := *(tmp15)
									if tmp16 != nil {
										tmp17 := *(tmp16)
										to.Bio = tmp17
									}
								}
							}
						}
					}
				}
			}
		}
	}
	tmp18 := (string)(from.Blog)
	to.Blog = tmp18
	tmp19 := (int64)(from.Collaborators)
	to.Collaborators = tmp19
	to.Company = from.Company
	to.CreatedAt = TimeToString(from.CreatedAt)
	tmp20 := (int64)(from.DiskUsage)
	to.DiskUsage = tmp20
	to.Email = from.Email
	tmp21 := (int64)(from.Followers)
	to.Followers = tmp21
	tmp22 := (int64)(from.Following)
	to.Following = tmp22
	to.GravatarID = from.GravatarID
	to.Hireable = from.Hireable
	tmp23 := (string)(from.HTMLURL)
	to.HTMLURL = tmp23
	tmp24 := (int64)(from.ID)
	to.ID = tmp24
	to.Location = from.Location
	to.Login = from.Login
	to.Name = from.Name
	tmp25 := (int64)(from.OwnedPrivateRepos)
	to.OwnedPrivateRepos = tmp25
	to.Plan = PlanToUserPlan(&(from.Plan))
	if from.PrivateGists != nil {
		tmp26 := *(from.PrivateGists)
		tmp27 := (int64)(tmp26)
		to.PrivateGists = tmp27
	}
	if from.PublicGists != nil {
		tmp28 := *(from.PublicGists)
		if tmp28 != nil {
			tmp29 := *(tmp28)
			tmp30 := (int64)(tmp29)
			to.PublicGists = tmp30
		}
	}
	if from.PublicRepos != nil {
		tmp31 := *(from.PublicRepos)
		if tmp31 != nil {
			tmp32 := *(tmp31)
			if tmp32 != nil {
				tmp33 := *(tmp32)
				tmp34 := (int64)(tmp33)
				to.PublicRepos = tmp34
			}
		}
	}
	tmp35 := (int64)(from.TotalPrivateRepos)
	to.TotalPrivateRepos = tmp35
	to.Type = from.Type
	tmp36 := (string)(from.URL)
	to.URL = tmp36
	return to
}

// PlanToUserPlan :
func PlanToUserPlan(from *github.Plan) *def.UserPlan {
	to := &def.UserPlan{}
	tmp37 := (int64)(from.Collaborators)
	to.Collaborators = tmp37
	to.Name = from.Name
	tmp38 := (int64)(from.PrivateRepos)
	to.PrivateRepos = tmp38
	tmp39 := (int64)(from.Space)
	to.Space = tmp39
	return to
}

<! - ## Ramasser les feuilles mortes !-- !-- misc !-- ! ---- Que faire si je veux écrire à la main une partie de la fonction de conversion? ! ---- Qu'est-ce que la prise en charge du pointeur? ! ---- Quel est le support de l'interface? ! --- Quelle est la correspondance des tranches? ! ---- Quelle est la correspondance du type alias (nouveau type)? ! ---- Qu'est-ce que le support intégré? ! ---- Quelle est la correspondance des champs non convertibles? ! --- Prend en charge les fluctuations de notation (Id, ID, ...) ! --- Prend-il en charge plusieurs fonctions d'argument? ! --- Que se passe-t-il si une nouvelle fonction manuscrite nécessite un argument supplémentaire? ! ---- Est-ce que cela fonctionne si vous souhaitez utiliser une fonction que vous avez définie au milieu de l'omission de la distribution? ->

Recommended Posts

Écrivons du code python qui analyse le code go et génère du code go
Essayez d'écrire du code python pour générer du code go - Essayez de porter JSON-to-Go et ainsi de suite
Écrivons un programme Python et exécutons-le
Écrivons python avec cinema4d.
Créez le code qui renvoie "A et prétendant B" en python
Comment écrire une classe méta qui prend en charge à la fois python2 et python3
Ce code Python n'a pas de classes ...
[Python] Maîtrisons tout et tout
[PEP8] Reprenez le code source Python et écrivez-le proprement
[Python] Écrivons brièvement la notation d'inclusion
Ceci et cela des propriétés python
Ecrire le code de test du sélénium en python
[Python] J'ai écrit un code simple qui génère automatiquement AA (Ascii Art)
Jetons JavaScript et écrivons un front-end Web en Python!
Créons une base de données clients où le code QR est automatiquement émis en Python
Lecture de code de faker, une bibliothèque qui génère des données de test en Python
Installation du code Visual Studio et installation de python
Écrivez du code FizzBuzz piloté par les tests à l'aide de Python doctest.
Essayons gRPC avec Go et Docker
Ecrire le fichier O_SYNC en C et Python
Lire et écrire des fichiers JSON avec Python
Compressez les données python et écrivez sur sqlite
Un joli nimporter qui connecte nim et python
Implémenter la récurrence et l'exploration commémoratives dans Python and Go
Code Python qui supprime les espaces contigus en un
Comparez les vitesses d'analyse XML avec Python et Go
[Python3] Lecture et écriture avec isoformat datetime avec json
Liens et mémos de chaînes de code de caractères Python
Contrôlons les moteurs et capteurs EV3 avec Python
Traitement Y / n avec bash, Python et Go
Liste de code Python à déplacer et à mémoriser
Créons une application capable de rechercher des images similaires avec Python et Flask Part1
Créons une application capable de rechercher des images similaires avec Python et Flask Part2
mong - J'ai essayé de porter le code qui génère de manière aléatoire le nom du conteneur Docker vers Python -