рдСрдЯреЛ рдЙрддреНрдкрдиреНрди рдХреЛрдб рдФрд░ рдкреНрд░рд▓реЗрдЦрди рдХреЗ рд╕рд╛рде рдЧреЛрд▓рдВрдЧ рдПрдкреАрдЖрдИ рд╕рд░реНрд╡рд░ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЗ

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ рдореИрдВ рдЖрдкрдХреЛ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрддрд╛рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдЖрдк рдЗрд╕рдХреЗ рд▓рд┐рдП рдкреНрд░рд▓реЗрдЦрди рдХреЗ рд╕рд╛рде рдЧреЛрд▓рдВрдЧ рднрд╛рд╖рд╛ рдореЗрдВ рдХреИрд╕реЗ рдЬрд▓реНрджреА рдФрд░ рдЖрд╕рд╛рдиреА рд╕реЗ рдПрдХ рд╡реЗрдм рд╕рд░реНрд╡рд░ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред рдФрд░ рдЙрдирдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд▓рд┐рдП рджреГрд╖реНрдЯрд┐рдХреЛрдг рдФрд░ рдЙрдкрдХрд░рдг рдХреНрдпрд╛ рд╣реИрдВ


рдЖрдЬ рд╣рдо рдЗрди рддреИрдпрд╛рд░ рдЙрдкрдХрд░рдгреЛрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░реЗрдВрдЧреЗ:



рд╕реНрд╡реИрдЧрд░ рдХреЛрдбрдЧреЗрди


рд╕рд╛рде рдЖрдЗрдП рд╢реБрд░реВ рдЕрдХрдбрд╝-api / рдЕрдХрдбрд╝-codegen рджреНрд╡рд╛рд░рд╛ рдкреНрд░рджрд╛рди рдХреА swagger.io ред рд╕реНрд╡реИрдЧрд░ рдХреЛрдбреЗрди рдУрдкрдирдПрдкреАрдЖрдИ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ (рдЬрд┐рд╕реЗ рдкрд╣рд▓реЗ рд╕реНрд╡реИрдЧрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдЬрд╛рдирд╛ рдЬрд╛рддрд╛ рдерд╛) рдореЗрдВ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рд╕реА рднреА рдПрдкреАрдЖрдИ рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рд╕реНрдЯрдмреНрд╕ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдПрд╕рдбреАрдХреЗ рдмрдирд╛рдХрд░ рдирд┐рд░реНрдорд╛рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╕рд░рд▓ рдмрдирд╛рддрд╛ рд╣реИ, рдЖрдкрдХреЛ рдмрд╕ рдЕрдкрдирд╛ рдПрдкреАрдЖрдИ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╣реИред


рдиреАрдЪреЗ рд╣рдорд╛рд░реА рд╕реНрд╡реИрдЧрд░ рдлрд╝рд╛рдЗрд▓ рдФрд░ рд╕реНрд╡реИрдЧрд░ рд╕рдВрдкрд╛рджрдХ рдореЗрдВ рдЗрд╕рдХреА рджреГрд╢реНрдп рдЙрдкрд╕реНрдерд┐рддрд┐ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИ, рдЬрд╣рд╛рдВ рд╣рдо рдПрдХ рдЪреАрдЬ рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдЙрддреНрдкрдиреНрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


swagger.json
{
  "swagger" : "2.0",
  "info" : {
    "version" : "1.0.0",
    "title" : "Swagger Petstore"
  },
  "host" : "localhost",
  "basePath" : "/v1",
  "tags" : [ {
    "name" : "pet"
  } ],
  "schemes" : [ "https", "http" ],
  "paths" : {
    "/pet" : {
      "post" : {
        "tags" : [ "pet" ],
        "summary" : "Add a new pet to the store",
        "operationId" : "addPet",
        "consumes" : [ "application/json" ],
        "produces" : [ "application/json" ],
        "parameters" : [ {
          "in" : "body",
          "name" : "body",
          "description" : "Pet object that needs to be added to the store",
          "required" : true,
          "schema" : {
            "$ref" : "#/definitions/Pet"
          }
        } ],
        "responses" : {
          "405" : {
            "description" : "Invalid input"
          }
        }
      }
    }
  },
  "definitions" : {
    "Category" : {
      "type" : "object",
      "properties" : {
        "id" : {
          "type" : "integer",
          "format" : "int64"
        },
        "name" : {
          "type" : "string"
        }
      }
    },
    "Tag" : {
      "type" : "object",
      "properties" : {
        "id" : {
          "type" : "integer",
          "format" : "int64"
        },
        "name" : {
          "type" : "string"
        }
      }
    },
    "Pet" : {
      "type" : "object",
      "required" : [ "name", "photoUrls" ],
      "properties" : {
        "id" : {
          "type" : "integer",
          "format" : "int64"
        },
        "category" : {
          "$ref" : "#/definitions/Category"
        },
        "name" : {
          "type" : "string",
          "example" : "doggie"
        },
        "photoUrls" : {
          "type" : "array",
          "items" : {
            "type" : "string"
          }
        },
        "tags" : {
          "type" : "array",
          "items" : {
            "$ref" : "#/definitions/Tag"
          }
        },
        "status" : {
          "type" : "string",
          "description" : "pet status in the store",
          "enum" : [ "available", "pending", "sold" ]
        }
      }
    }
  }
}

рд╕реНрд╡реИрдЧрд░ рдпреВрдЖрдИ рдореЗрдВ рджреГрд╢реНрдп рджреГрд╢реНрдп

Swagger UI


рд╕реНрд╡реИрдЧрд░ рд╕рдВрдкрд╛рджрдХ рд╕реЗ рдирд┐рд░реНрдпрд╛рдд рдХрд░реЗрдВ

Swagger Editor


рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдореЗрдВ рд╕рдорд╛рди рд╕рдВрд░рдЪрдирд╛ рдСрдмреНрдЬреЗрдХреНрдЯ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдлрд╝рд╛рдЗрд▓ go-client-generated/model_pet.goрдФрд░ go-server-server-generated/go/model_pet.goред


рдХреНрд▓рд╛рдЗрдВрдЯ рдФрд░ рд╕рд░реНрд╡рд░ рдореЗрдВ рдкрд╛рд▓рддреВ рд╕рдВрд░рдЪрдирд╛ рдореЙрдбрд▓
/*
 * Swagger Petstore
 *
 * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
 *
 * API version: 1.0.0
 * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
 */

package swagger

type Pet struct {
    Id int64           `json:"id,omitempty"`
    Category *Category `json:"category,omitempty"`
    Name string        `json:"name"`
    PhotoUrls []string `json:"photoUrls"`
    Tags []Tag         `json:"tags,omitempty"`
    // pet status in the store
    Status string `json:"status,omitempty"`
}

рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рдкрд╛рд╕ рдЕрдиреБрд░реЛрдз рднреЗрдЬрдиреЗ рдХреЗ рддрд░реАрдХреЗ рднреА рд╣реЛрддреЗ рд╣реИрдВ, рдФрд░ рд╕рд░реНрд╡рд░ рдЙрдиреНрд╣реЗрдВ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдВрдбрдкрд╛рдЗрдВрдЯ рдФрд░ рд╕реНрдЯрдмреНрд╕ рд╣реЛрддрд╛ рд╣реИред


рдЧреНрд░рд╛рд╣рдХ рд╡рд╛рд╕реНрддреБрдХрд▓рд╛


рд╕рд░реНрд╡рд░ рдЖрд░реНрдХрд┐рдЯреЗрдХреНрдЪрд░


, . , .


go-swagger


тАК тАФ тАКgo-swagger/go-swagger. go API- Swagger API: , .


  • Swagger
  • Swagger
  • , jsonschema swagger,
  • swagger go
  • swagger
  • ,


:


$ swagger generate server -f ./swagger.json

 

 


, , . .


тАФ go-swagger-service/restapi/configure_swagger_petstore.go. : handler', middleware, , , .


// This file is safe to edit. Once it exists it will not be overwritten

package restapi

import (
    "crypto/tls"
    "net/http"

    errors "github.com/go-openapi/errors"
    runtime "github.com/go-openapi/runtime"
    middleware "github.com/go-openapi/runtime/middleware"

    "go-generator/go-swagger-service/restapi/operations"
    "go-generator/go-swagger-service/restapi/operations/pet"
)

//go:generate swagger generate server --target ../../go-swagger --name SwaggerPetstore --spec ../../swagger.json

func configureFlags(api *operations.SwaggerPetstoreAPI) {
    // api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
}

func configureAPI(api *operations.SwaggerPetstoreAPI) http.Handler {
    // configure the api here
    api.ServeError = errors.ServeError

    // Set your custom logger if needed. Default one is log.Printf
    // Expected interface func(string, ...interface{})
    //
    // Example:
    // api.Logger = log.Printf

    api.JSONConsumer = runtime.JSONConsumer()

    api.JSONProducer = runtime.JSONProducer()

    if api.PetAddPetHandler == nil {
        api.PetAddPetHandler = pet.AddPetHandlerFunc(func(params pet.AddPetParams) middleware.Responder {
            return middleware.NotImplemented("operation pet.AddPet has not yet been implemented")
        })
    }

    api.ServerShutdown = func() {}

    return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}

// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
    // Make all necessary changes to the TLS configuration here.
}

// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix"
func configureServer(s *http.Server, scheme, addr string) {
}

// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation
func setupMiddlewares(handler http.Handler) http.Handler {
    return handler
}

// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics
func setupGlobalMiddleware(handler http.Handler) http.Handler {
    return handler
}

. 422 .


swagger . enum, required, type, maximum .


Pet
// Code generated by go-swagger; DO NOT EDIT.

package models

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

import (
    "encoding/json"
    "strconv"

    strfmt "github.com/go-openapi/strfmt"

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

// Pet pet
// swagger:model Pet
type Pet struct {

    // category
    Category *Category `json:"category,omitempty"`

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

    // name
    // Required: true
    Name *string `json:"name"`

    // photo urls
    // Required: true
    PhotoUrls []string `json:"photoUrls"`

    // pet status in the store
    // Enum: [available pending sold]
    Status string `json:"status,omitempty"`

    // tags
    Tags []*Tag `json:"tags"`
}

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

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

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

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

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

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

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

func (m *Pet) validateCategory(formats strfmt.Registry) error {

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

    if m.Category != nil {
        if err := m.Category.Validate(formats); err != nil {
            if ve, ok := err.(*errors.Validation); ok {
                return ve.ValidateName("category")
            }
            return err
        }
    }

    return nil
}

func (m *Pet) validateName(formats strfmt.Registry) error {

    if err := validate.Required("name", "body", m.Name); err != nil {
        return err
    }

    return nil
}

func (m *Pet) validatePhotoUrls(formats strfmt.Registry) error {

    if err := validate.Required("photoUrls", "body", m.PhotoUrls); err != nil {
        return err
    }

    return nil
}

var petTypeStatusPropEnum []interface{}

func init() {
    var res []string
    if err := json.Unmarshal([]byte(`["available","pending","sold"]`), &res); err != nil {
        panic(err)
    }
    for _, v := range res {
        petTypeStatusPropEnum = append(petTypeStatusPropEnum, v)
    }
}

const (

    // PetStatusAvailable captures enum value "available"
    PetStatusAvailable string = "available"

    // PetStatusPending captures enum value "pending"
    PetStatusPending string = "pending"

    // PetStatusSold captures enum value "sold"
    PetStatusSold string = "sold"
)

// prop value enum
func (m *Pet) validateStatusEnum(path, location string, value string) error {
    if err := validate.Enum(path, location, value, petTypeStatusPropEnum); err != nil {
        return err
    }
    return nil
}

func (m *Pet) validateStatus(formats strfmt.Registry) error {

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

    // value enum
    if err := m.validateStatusEnum("status", "body", m.Status); err != nil {
        return err
    }

    return nil
}

func (m *Pet) validateTags(formats strfmt.Registry) error {

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

    for i := 0; i < len(m.Tags); i++ {
        if swag.IsZero(m.Tags[i]) { // not required
            continue
        }

        if m.Tags[i] != nil {
            if err := m.Tags[i].Validate(formats); err != nil {
                if ve, ok := err.(*errors.Validation); ok {
                    return ve.ValidateName("tags" + "." + strconv.Itoa(i))
                }
                return err
            }
        }

    }

    return nil
}

// MarshalBinary interface implementation
func (m *Pet) MarshalBinary() ([]byte, error) {
    if m == nil {
        return nil, nil
    }
    return swag.WriteJSON(m)
}

// UnmarshalBinary interface implementation
func (m *Pet) UnmarshalBinary(b []byte) error {
    var res Pet
    if err := swag.ReadJSON(b, &res); err != nil {
        return err
    }
    *m = res
    return nil
}


$ run ./go-swagger-service/cmd/swagger-petstore-server/main.go --port 8090


:


$ swagger generate client -f ./swagger.json

 

 


, api-sdk. , , .


// Code generated by go-swagger; DO NOT EDIT.

package pet

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

import (
    "github.com/go-openapi/runtime"

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

// New creates a new pet API client.
func New(transport runtime.ClientTransport, formats strfmt.Registry) *Client {
    return &Client{transport: transport, formats: formats}
}

/*
Client for pet API
*/
type Client struct {
    transport runtime.ClientTransport
    formats   strfmt.Registry
}

/*
AddPet adds a new pet to the store
*/
func (a *Client) AddPet(params *AddPetParams) error {
    // TODO: Validate the params before sending
    if params == nil {
        params = NewAddPetParams()
    }

    _, err := a.transport.Submit(&runtime.ClientOperation{
        ID:                 "addPet",
        Method:             "POST",
        PathPattern:        "/pet",
        ProducesMediaTypes: []string{"application/json"},
        ConsumesMediaTypes: []string{"application/json"},
        Schemes:            []string{"http", "https"},
        Params:             params,
        Reader:             &AddPetReader{formats: a.formats},
        Context:            params.Context,
        Client:             params.HTTPClient,
    })
    if err != nil {
        return err
    }
    return nil
}

// SetTransport changes the transport on the client
func (a *Client) SetTransport(transport runtime.ClientTransport) {
    a.transport = transport
}

. Generate a spec from source code.


. , swagger .


grpc-gateway


swagger . grpc-ecosystem/grpc-gateway тАФ тАК protoc. gRPC -, RESTful JSON API gRPC. gRPC.


HTTP + JSON gRPC. API gRPC RESTful .


grpc-gateway reverse-proxy

grpc-gateway   reverse-proxy


swagger.json protoc-gen-swagger.


. .


$ go install \
    github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
    github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \
    github.com/golang/protobuf/protoc-gen-go

 .proto


protobuf
syntax = "proto3";

import "google/api/annotations.proto";
import "google/protobuf/empty.proto";

package pet;

message AddPetRequest {
    int64 id = 1;
    message Category {
        int64 id = 1;
        string name = 2;
    }
    Category category = 2;
    string name = 3;
    repeated string photo_urls = 4;
    message Tag {
        int64 id = 1;
        string name = 2;
    }
    repeated Tag tags = 5;
    //pet status in the store
    enum Status {
        available = 0;
        pending = 1;
        sold = 2;
    }
    Status status = 6;
}

service PetService {
    rpc AddPet (AddPetRequest) returns (google.protobuf.Empty) {
        option (google.api.http) = {
            post: "/pet"
            body: "*"
        };
    }
}

gRPC stub:


$ protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --go_out=plugins=grpc:. \
  pet.proto

pet.pb.go. reverse-proxy protoc-gen-grpc-gateway:


$ protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --grpc-gateway_out=logtostderr=true:. \
  pet.proto

pet.pb.gw.go . gateway. pet.swagger.json protoc-gen-swagger:


$ protoc -I/usr/local/include -I. \
  -I$GOPATH/src \
  -I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
  --swagger_out=logtostderr=true:. \
  pet.proto

PetServiceServer.


// PetServiceServer is the server API for PetService service.
type PetServiceServer interface {
   AddPet(context.Context, *AddPetRequest) (*empty.Empty, error)
}

:


main.go
package pet

import (
    "context"
    "flag"
    "net/http"

    "github.com/golang/glog"
    "github.com/grpc-ecosystem/grpc-gateway/runtime"
    gw "grpc-gateway/pet"
    "grpc-gateway/service"
)

func run() error {
    ctx := context.Background()
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()

    mux := runtime.NewServeMux()
    err := gw.RegisterPetServiceHandlerServer(ctx, mux, &service.PetService{})
    if err != nil {
        return err
    }

    return http.ListenAndServe(":8081", mux)
}

func main() {
    flag.Parse()
    defer glog.Flush()

    if err := run(); err != nil {
        glog.Fatal(err)
    }
}

, . , c grpc-gateway


Swag


Swag Go Swagger 2.0. - Go. swaggo/swag Go ( Swagger UI).


:



Swagger , endpoint'. .


API
// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler server.
// @termsOfService http://swagger.io/terms/

// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io

// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /api/v1
// @query.collection.format multi

// @securityDefinitions.basic BasicAuth

// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization

// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.implicit OAuth2Implicit
// @authorizationurl https://example.com/oauth/authorize
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.password OAuth2Password
// @tokenUrl https://example.com/oauth/token
// @scope.read Grants read access
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information

// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
// @tokenUrl https://example.com/oauth/token
// @authorizationurl https://example.com/oauth/authorize
// @scope.admin Grants read and write access to administrative information

// @x-extension-openapi {"example": "value on a json format"}

func main() {
    // ... 
}

API-
// ShowAccount godoc
// @Summary Show a account
// @Description get string by ID
// @ID get-string-by-int
// @Accept  json
// @Produce  json
// @Param id path int true "Account ID"
// @Success 200 {object} model.Account
// @Header 200 {string} Token "qwerty"
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts/{id} [get]

func (c *ExampleController) ShowAccount(ctx *contex.Context) {
    // ...
}

github.


, , .



рдЕрдм рд╣рдордиреЗ рд╕реНрд╡реИрдЧрд░ рдкреНрд░рд▓реЗрдЦрди рдХреЗ рд╕рд╛рде рд╕рдВрднрд╛рд╡рд┐рдд рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреА рд╣рдорд╛рд░реА рд╕рдордЭ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд┐рдпрд╛ рд╣реИред рд╣рдордиреЗ рдХреЛрдб рдХреЛ рдбрд┐рдЬрд╛рдЗрди рдХрд░рдиреЗ рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдкреНрд░рд▓реЗрдЦрди рд╕рдВрдХрд▓рд┐рдд рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЛрдВ рдХреА рдЬрд╛рдВрдЪ рдХреАред


рдФрд░ рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдХреБрдЫ рдиреЗ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЕрдкрдиреА рдирдИ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреЗ рдПрдкреАрдЖрдИ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреБрдХреНрдд рд╡рд┐рдзрд┐ рдЪреБрдирд╛ рд╣реИ, рдпрд╛ рд╡рд░реНрддрдорд╛рди рд▓реЛрдЧреЛрдВ рдХреЗ рдЦрд┐рд▓рд╛рдл рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд┐рдП рдЧрдП рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреА рдХреЛрд╢рд┐рд╢ рдХреА рд╣реИред


All Articles