Beego endpoint can't find templatefile... but i'm not using template - go

I'm having trouble to create a endpoint on a Beego application
So, I just put some object information on the returned JSON:
// GetOne ...
// #Title GetOne
// #Description get Migration by id
// #Param id path string true "The key for staticblock"
// #Success 200 {object} models.Migration
// #Failure 403 :id is empty
// #router /:id [get]
func (c *MigrationController) GetOne() {
val, err := mg.Data["json"] = map[string]string{
"MigrationId": c.MigrationId
"Status": c.Status
"Created": c.Created
"Updated": c.Updated
}
if err != nil {
log.Debug("Fail - GetOne: %v", err)
} else {
mg.ServeJSON()
}
When I tried to call the endpoint, I get this
Handler crashed with error can't find templatefile in the path:views/migrationcontroller/getone.tpl
I'm not using these templates anywhere in the whole code...
I'm not familiar with this framework, someone can help me?

You should use ServeJSON() with current controller.
func (c *MigrationController) GetOne() {
defer c.ServeJSON()
...
}

Related

rpc error: code = Unavailable desc = error reading from server: EOF resulting in a panic runtime error: invalid memory address error on goland

I am new Stack Overflow and this is my first question so I'm very open and happy to make any improvements to it :)
I'm having an issue when I run a test method to unlike an artwork. I have a method that enables a user to like a specific artwork which is categorised by their own separate artwork uuid's, and the method works perfectly. I have a console.proto file where I specify the necessary request and response messages for both the LikeArtwork and UnlikeArtwork methods, and have created the methods themselves in a methods.go file. I have other methods that I am testing and they all seem to work perfectly except for the UnlikeArtwork method.
I have a followUser and UnfollowUser method, which both work in exactly the same way, except in the UnfollowUser method, a user is just removed from the "followers" slice/array that contains all of the followers. The concept is exactly the same for the likeArtwork and UnlikeArtwork methods where users are either added or removed from the "likes" slice/array, which is why I am so confused as to why it is causing my User's server to crash with the error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x58 pc=0x104d0b844]
My console.proto is defined as follows:
service Service {
// Registers a user on the hockney platform/ This entails creating the user
// using the CreateUser method and uploading their profile picture using the
// UploadProfilePicture method/ Down the line, this could also entail the
// creation of a custodian wallet for users as well/
rpc RegisterUser (RegisterUserRequest) returns (RegisterUserResponse) {}
// Follow user takes the user of the current session and updates their details
// and that of the followee to reflect the following
rpc FollowUser (FollowUserRequest) returns (FollowUserResponse) {}
// Like an artwork
rpc LikeArtwork (LikeArtworkRequest) returns (LikeArtworkResponse) {}
// Unfollow a user
rpc UnfollowUser (UnfollowUserRequest) returns (UnfollowUserResponse) {}
// Unlike an artwork
rpc UnlikeArtwork (UnlikeArtworkRequest) returns (UnlikeArtworkResponse) {}
// New UnLike an artwork method
rpc UnLikeArtwork (UnLikeArtworkRequest) returns (UnLikeArtworkResponse) {}
}
and,
// Request message for the FollowUser method
message FollowUserRequest {
// The user of the current session
// Format: users/{username}
string current_user = 1;
// The user to follow
// Format: users/{username}
string user_to_follow = 2;
}
// Response message for the FollowUser method
// Reserved for future use...
message FollowUserResponse {}
// Request message for LikeArtwork
message LikeArtworkRequest {
// The user that likes
// Format: users/{username}
string user = 1;
// The artwork that has been liked
// Format: artworks/{uuid}
string artwork = 2;
}
// Response message for LikeArtwork
message LikeArtworkResponse {}
// Request message for the UnfollowUser method
message UnfollowUserRequest {
// The user of the current session
// Format: users/{username}
string current_user = 1;
// The user to unfollow
// Format: users/{username}
string user_to_unfollow = 2;
}
// Response message for UnfollowUser method
message UnfollowUserResponse {}
// Request message for the UnlikeArtwork method
message UnlikeArtworkRequest {
// The user that unlikes
// Format: users/{username}
string user = 1;
// The artwork that has been unliked
// Format: artworks/{uuid}
string artwork_to_unlike = 2;
}
// Response message for the UnlikeArtwork method
message UnlikeArtworkResponse {}
The method I wrote to unlike an artwork works in exactly the same way as the LikeArtwork method, however, a user is removed from the "likes" slice. This occurs when currentUser.Likes = append(...)... . I don't think thats where the issue lies, since running this code through the Goland debugger, the error seems to occur before it gets to this part of the function. The function for this method is shown below:
func (s *myService) UnlikeArtwork(ctx context.Context, req *pb.UnlikeArtworkRequest) (*pb.UnlikeArtworkResponse, error) {
currentUser, err := clients.Users.GetUser(ctx, &pbUsers.GetUserRequest{
Name: req.GetUser(),
ReadMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
// Set new likes
//currentUser.Following = append(currentUser.Following[:sort.StringSlice(currentUser.Following).Search(req.GetUserToUnfollow())], currentUser.Following[sort.StringSlice(currentUser.Following).Search(req.GetUserToUnfollow())+1:]...)
currentUser.Likes = append(currentUser.Likes[:sort.StringSlice(currentUser.Likes).Search(req.GetUser())], currentUser.Likes[sort.StringSlice(currentUser.Likes).Search(req.GetUser())+1:]...)
// Update the current_user
_, err = clients.Users.UpdateUser(ctx, &pbUsers.UpdateUserRequest{
User: currentUser,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
currentArtwork, err := clients.Artworks.GetArtwork(ctx, &pbArtworks.GetArtworkRequest{
Name: req.GetArtworkToUnlike(),
ReadMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
// Set the likes
//userToUnfollow.Followers = append(userToUnfollow.Followers[:sort.StringSlice(userToUnfollow.Followers).Search(req.GetCurrentUser())], userToUnfollow.Followers[sort.StringSlice(userToUnfollow.Followers).Search(req.GetCurrentUser())+1:]...)
currentArtwork.Likes = append(currentArtwork.Likes[:sort.StringSlice(currentArtwork.Likes).Search(req.GetArtworkToUnlike())], currentArtwork.Likes[sort.StringSlice(currentArtwork.Likes).Search(req.GetArtworkToUnlike())+1:]...)
// Update the current artwork
_, err = clients.Artworks.UpdateArtwork(ctx, &pbArtworks.UpdateArtworkRequest{
Artwork: currentArtwork,
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"likes"}},
})
if err != nil {
return nil, err
}
return &pb.UnlikeArtworkResponse{}, nil
}
The test was then run in a methods_test.go file where the inputs to the function and the function itself was simulated.
func TestMyService_UnlikeArtwork(t *testing.T) {
req := pbConsole.UnlikeArtworkRequest{
User: "PurpleRaine",
ArtworkToUnlike: "artworks/0cca6063-7b6f-464a-ac88-dff8679a3905",
}
// Run a method
res, err := client.UnlikeArtwork(context.Background(), &req)
if err != nil {
t.Error(err)
}
log.Println(logging.Entry{Message: res.String()})
}
The output of the test is:
methods_test.go:111: rpc error: code = Unavailable desc = error reading from server: EOF
methods_test.go:114: {"message":"\u003cnil\u003e","severity":"INFO"}
--- FAIL: TestMyService_UnLikeArtwork (0.01s)
FAIL
and this happens along with the server crash error listed at the beginning of this question.
Additional Information:
My user's server is configured as follows:
package main
import (
"context"
"fmt"
pb "github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1"
"google.golang.org/grpc"
"hock.ap.resources.users.v1/internal/logging"
"hock.ap.resources.users.v1/internal/methods"
"log"
"net"
"os"
)
// clients is a global clients, initialized once per cloud run instance.
var ()
func init() {
// Disable log prefixes such as the default timestamp.
// Prefix text prevents the message from being parsed as JSON.
// A timestamp is added when shipping logs to Cloud Logging.
log.SetFlags(0)
// TODO: implement when needed
// Ensure that required envs exist.
//if os.Getenv("ENV") == "" {
// log.Fatal("ENV env not set.")
//}
}
func main() {
log.Println(&logging.Entry{Message: "starting server...", Severity: logging.NOTICE})
port := os.Getenv("USERS_PORT")
if port == "" {
port = "8080"
log.Println(&logging.Entry{Message: "Defaulting to port " + port, Severity: logging.WARNING})
}
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
log.Fatalf("net.Listen: %v", err)
}
grpcServer := grpc.NewServer(grpc.UnaryInterceptor(serverInterceptor))
pb.RegisterUsersServiceServer(grpcServer, &methods.MyService{})
if err = grpcServer.Serve(listener); err != nil {
log.Fatal(err)
}
}
// serverInterceptor is an example of a Server Interceptor which could be used to 'inject'
// for example logs and/or tracing details to incoming server requests.
// Add this method to your grpc server connection, for example
// grpcServer := grpc.NewServer(grpc.UnaryInterceptor(serverInterceptor))
//
// pb.RegisterServiceServer(grpcServer, &myService{})
func serverInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// Calls the handler
h, err := handler(ctx, req)
if err != nil {
log.Println(&logging.Entry{Message: fmt.Sprintf("%v", req), Severity: logging.DEBUG, Ctx: ctx})
log.Println(&logging.Entry{Message: err.Error(), Severity: logging.WARNING, Ctx: ctx})
}
_ = info
return h, err
}
Edited: Here is the full stack trace when the crash occurs:
goroutine 54 [running]:
cloud.google.com/go/firestore.(*DocumentSnapshot).DataTo(0x0?, {0x104e28820?, 0x140000d02c0?})
/Users/michaelaltshuler/go/pkg/mod/cloud.google.com/go/firestore#v1.6.1/document.go:112 +0x24
hock.ap.resources.users.v1/internal/methods.(*MyService).GetUser(0x1400011f9a8?, {0x104e4ea60, 0x14000102750}, 0x14?)
/Users/michaelaltshuler/GolandProjects/hockney/products/hock/ap/resources/users/v1/internal/methods/methods.go:59 +0x84
github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1._UsersService_GetUser_Handler.func1({0x104e4ea60, 0x14000102750}, {0x104dd2d40?, 0x14000046340})
/Users/michaelaltshuler/GolandProjects/hockney/protobuf/go/hock/ap/resources/users/v1/users_grpc.pb.go:212 +0x74
main.serverInterceptor({0x104e4ea60?, 0x14000102750}, {0x104dd2d40, 0x14000046340}, 0x1400011faa8?, 0x104a98c24?)
/Users/michaelaltshuler/GolandProjects/hockney/products/hock/ap/resources/users/v1/server.go:62 +0x3c
github.com/jaebrownn/hockney/protobuf/go/hock/ap/resources/users/v1._UsersService_GetUser_Handler({0x104dd7cc0?, 0x1052cdd08}, {0x104e4ea60, 0x14000102750}, 0x14000140000, 0x104e44430)
/Users/michaelaltshuler/GolandProjects/hockney/protobuf/go/hock/ap/resources/users/v1/users_grpc.pb.go:214 +0x138
google.golang.org/grpc.(*Server).processUnaryRPC(0x140000d63c0, {0x104e52278, 0x14000003860}, 0x140005a4000, 0x140000a08a0, 0x10528e3f8, 0x0)
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:1295 +0x9c4
google.golang.org/grpc.(*Server).handleStream(0x140000d63c0, {0x104e52278, 0x14000003860}, 0x140005a4000, 0x0)
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:1636 +0x82c
google.golang.org/grpc.(*Server).serveStreams.func1.2()
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:932 +0x84
created by google.golang.org/grpc.(*Server).serveStreams.func1
/Users/michaelaltshuler/go/pkg/mod/google.golang.org/grpc#v1.48.0/server.go:930 +0x290
And when navigating to my getUser method from this stack trace it takes me to the line err = dsnap.DataTo(user) in the code below:
dsnap, err := clients.Firestore.Doc(req.GetName()).Get(ctx)
//user message to return
user := &pb.User{}
err = dsnap.DataTo(user) // This line
if err != nil {
return nil, err
}
I know this is a very long winded way of asking for help with this question. I have found very little resources online to deal with this issue, and I'm hoping this makes some sense and someone could guide me in the right direction.
Thank you!
I seemed to have fixed the problem and it was a lot more simple than I originally thought. When writing the test method I was specifying the user as User: "UserID", however, the server expected a path to the firestore document ref, and needed the input User: "user/UserID".

Go-Gin binding data with one-to-many relationship

I'm new to Golang and Gin framework, I have created two models
type Product struct {
gorm.Model
Name string
Media []Media
}
type Media struct {
gorm.Model
URI string
ProductID uint
}
and I send a POST request to save a new product, the body was:
{
"name": "Product1",
"media": [
"https://server.com/image1",
"https://server.com/image2",
"https://server.com/image3",
"https://server.com/video1",
"https://server.com/video2"
]
}
And I save a new product using this code
product := Product{}
if err := context.ShouldBindJSON(product); err != nil { // <-- here the error
context.String(http.StatusBadRequest, fmt.Sprintf("err: %s", err.Error()))
return
}
tx := DB.Create(&product)
if tx.Error != nil {
context.String(http.StatusBadRequest, fmt.Sprintf("err: %s", tx.Error))
return
}
the return error message is
err: json: cannot unmarshal string into Go struct field Product.Media of type models.Media
I know that ShouldBindJSON can't convert media-string to media-object, but what is the best practice to do this?
Your payload doesn't match the model. In the JSON body, media is an array of strings, whereas in the model it's a struct with two fields and the embedded gorm model.
If you can't change anything of your current setup, implement UnmarshalJSON on Media and set the URI field from the raw bytes. In the same method you may also initialize ProductID to something (if needed).
func (m *Media) UnmarshalJSON(b []byte) error {
m.URI = string(b)
return nil
}
Then the binding will work as expected:
product := Product{}
// pass a pointer to product
if err := context.ShouldBindJSON(&product); err != nil {
// handle err ...
return
}
fmt.Println(product) // {Product1 [{"https://server.com/image1" 0} ... }

Cannot bind POST body to URL in Go

I'm trying to make a simple API call to the pokemon API through reaching a POST request that I'm serving with Echo.
I'm sending a POST request to "localhost:8000/pokemon" with the body { "pokemon": "pikachu" } where the BODY is reattached to the request through ioutil changing the request to be made with the body: "localhost:8000/pokemon/pikachu".
The POST request works by responding with some JSON, but the call being made is only to "localhost:8000/pokemon", and it seems the body isn't added to the URL.
I think there is something wrong with the binding here u := new(pokemon)
Anyone have any ideas?
func main() {
e := echo.New() // Middleware
e.Use(middleware.Logger()) // Logger
e.Use(middleware.Recover())
//CORS
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE},
}))
// Root route => handler
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!\n")
})
e.POST("/pokemon", controllers.GrabPrice) // Price endpoint
// Server
e.Logger.Fatal(e.Start(":8000"))
}
type pokemon struct { pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}
// GrabPrice - handler method for binding JSON body and scraping for stock price
func GrabPrice(c echo.Context) (err error) {
// Read the Body content
var bodyBytes []byte
if c.Request().Body != nil {
bodyBytes, _ = ioutil.ReadAll(c.Request().Body)
}
// Restore the io.ReadCloser to its original state
c.Request().Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
u := new(pokemon)
er := c.Bind(u) // bind the structure with the context body
// on no panic!
if er != nil {
panic(er)
}
// company ticker
ticker := u.pokemon
print("Here", string(u.pokemon))
// yahoo finance base URL
baseURL := "https://pokeapi.co/api/v2/pokemon"
print(baseURL + ticker)
// price XPath
//pricePath := "//*[#name=\"static\"]"
// load HTML document by binding base url and passed in ticker
doc, err := htmlquery.LoadURL(baseURL + ticker)
// uh oh :( freak out!!
if err != nil {
panic(err)
}
// HTML Node
// from the Node get inner text
price := string(htmlquery.InnerText(doc))
return c.JSON(http.StatusOK, price)
}
Adding to what already answered by #mkopriva and #A.Lorefice
Yes you need to ensure that the variable are exported, for the binding to work properly.
Since underlay process of binding actually using reflection mechanism on the struct. See this documentation, scroll into Structs section to see what it is.
type pokemon struct {
Pokemon string `json:"pokemon" form:"pokemon" query:"pokemon"`
}

How to create a generic request validation middleware

What I want to achieve here is to create a very generic middleware called, Expects that actually validates the current request according to the parameters provided. It will raise a Bad Request if the required params are not present or are empty. In Python (Flask) this would be very simple like:
#app.route('/endpoint', methods=['POST'])
#expects(['param1', 'param2'])
def endpoint_handler():
return 'Hello World'
The definition of expects would look like this (a very minimal example):
def expects(fields):
def decorator(view_function):
#wraps(view_function)
def wrapper(*args, **kwargs):
# get current request data
data = request.get_json(silent=True) or {}
for f in fields:
if f not in data.keys():
raise Exception("Bad Request")
return view_function(*args, **kwargs)
return wrapper
return decorator
I am just confused a little about how would I achieve that in Go. What I tried so far is:
type RequestParam interface {
Validate() (bool, error)
}
type EndpointParamsRequired struct {
SomeParam string `json:"some_param"`
}
func (p *EndpointParamsRequired) Validate() {
// My validation logic goes here
if len(p.SomeParam) == 0 {
return false, "Missing field"
}
}
func Expects(p RequestParam, h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Check if present in JSON request
// Unmarshall JSON
...
if _, err := p.Validate(); err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "Bad request: %s", err)
return
}
}
}
and from main.go file:
func main() {
var (
endopintParams EndpointParamsRequired
)
r.HandleFunc("/endpoint", Expects(&endopintParams, EndpointHandler)).Methods("POST")
}
It actually works for the first time and validates the request, but after one valid request all the consecutive requests are successful even if the json does not contain the required param. Does that have anything to do with the global endopintParams I'm creating?

Batch requests to GCP Compute using Golang client library

Is it possible to perform batch requests using the Google API Client Library for Go?
More precisely, I'd like to delete some disks; it would be great if I could avoid a request for each disk.
The batch request feature I'm referring to:
https://cloud.google.com/compute/docs/api/how-tos/batch
The client library I'm using:
https://github.com/googleapis/google-api-go-client
It seems there's also another client library, but without support for the Compute Engine, only metadata (?):
https://github.com/GoogleCloudPlatform/google-cloud-go
I'm not sure but i think it can be done since we can dlete a disk using API call.
A batch request consists of multiple API calls combined into one HTTP request, which can be sent to the batchPath specified in the API discovery document. The default path is /batch/api_name/api_version.
Example take a look on the Format of a batch request Here
// method id "compute.disks.delete":
type DisksDeleteCall struct {
s *Service
project string
zone string
disk string
urlParams_ gensupport.URLParams
ctx_ context.Context
header_ http.Header
}
// Delete: Deletes the specified persistent disk. Deleting a disk
// removes its data permanently and is irreversible. However, deleting a
// disk does not delete any snapshots previously made from the disk. You
// must separately delete snapshots.
// For details, see https://cloud.google.com/compute/docs/reference/latest/disks/delete
func (r *DisksService) Delete(project string, zone string, disk string) *DisksDeleteCall {
c := &DisksDeleteCall{s: r.s, urlParams_: make(gensupport.URLParams)}
c.project = project
c.zone = zone
c.disk = disk
return c
}
// RequestId sets the optional parameter "requestId": An optional
// request ID to identify requests. Specify a unique request ID so that
// if you must retry your request, the server will know to ignore the
// request if it has already been completed.
//
// For example, consider a situation where you make an initial request
// and the request times out. If you make the request again with the
// same request ID, the server can check if original operation with the
// same request ID was received, and if so, will ignore the second
// request. This prevents clients from accidentally creating duplicate
// commitments.
//
// The request ID must be a valid UUID with the exception that zero UUID
// is not supported (00000000-0000-0000-0000-000000000000).
func (c *DisksDeleteCall) RequestId(requestId string) *DisksDeleteCall {
c.urlParams_.Set("requestId", requestId)
return c
}
// Fields allows partial responses to be retrieved. See
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse
// for more information.
func (c *DisksDeleteCall) Fields(s ...googleapi.Field) *DisksDeleteCall {
c.urlParams_.Set("fields", googleapi.CombineFields(s))
return c
}
// Context sets the context to be used in this call's Do method. Any
// pending HTTP request will be aborted if the provided context is
// canceled.
func (c *DisksDeleteCall) Context(ctx context.Context) *DisksDeleteCall {
c.ctx_ = ctx
return c
}
// Header returns an http.Header that can be modified by the caller to
// add HTTP headers to the request.
func (c *DisksDeleteCall) Header() http.Header {
if c.header_ == nil {
c.header_ = make(http.Header)
}
return c.header_
}
func (c *DisksDeleteCall) doRequest(alt string) (*http.Response, error) {
reqHeaders := make(http.Header)
for k, v := range c.header_ {
reqHeaders[k] = v
}
reqHeaders.Set("User-Agent", c.s.userAgent())
var body io.Reader = nil
c.urlParams_.Set("alt", alt)
c.urlParams_.Set("prettyPrint", "false")
urls := googleapi.ResolveRelative(c.s.BasePath, "{project}/zones/{zone}/disks/{disk}")
urls += "?" + c.urlParams_.Encode()
req, _ := http.NewRequest("DELETE", urls, body)
req.Header = reqHeaders
googleapi.Expand(req.URL, map[string]string{
"project": c.project,
"zone": c.zone,
"disk": c.disk,
})
return gensupport.SendRequest(c.ctx_, c.s.client, req)
}
// Do executes the "compute.disks.delete" call.
// Exactly one of *Operation or error will be non-nil. Any non-2xx
// status code is an error. Response headers are in either
// *Operation.ServerResponse.Header or (if a response was returned at
// all) in error.(*googleapi.Error).Header. Use googleapi.IsNotModified
// to check whether the returned error was because
// http.StatusNotModified was returned.
func (c *DisksDeleteCall) Do(opts ...googleapi.CallOption) (*Operation, error) {
gensupport.SetOptions(c.urlParams_, opts...)
res, err := c.doRequest("json")
if res != nil && res.StatusCode == http.StatusNotModified {
if res.Body != nil {
res.Body.Close()
}
return nil, &googleapi.Error{
Code: res.StatusCode,
Header: res.Header,
}
}
if err != nil {
return nil, err
}
defer googleapi.CloseBody(res)
if err := googleapi.CheckResponse(res); err != nil {
return nil, err
}
ret := &Operation{
ServerResponse: googleapi.ServerResponse{
Header: res.Header,
HTTPStatusCode: res.StatusCode,
},
}
target := &ret
if err := gensupport.DecodeResponse(target, res); err != nil {
return nil, err
}
return ret, nil
// {
// "description": "Deletes the specified persistent disk. Deleting a disk removes its data permanently and is irreversible. However, deleting a disk does not delete any snapshots previously made from the disk. You must separately delete snapshots.",
// "httpMethod": "DELETE",
// "id": "compute.disks.delete",
// "parameterOrder": [
// "project",
// "zone",
// "disk"
// ],
// "parameters": {
// "disk": {
// "description": "Name of the persistent disk to delete.",
// "location": "path",
// "required": true,
// "type": "string"
// },
// "project": {
// "description": "Project ID for this request.",
// "location": "path",
// "pattern": "(?:(?:[-a-z0-9]{1,63}\\.)*(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?):)?(?:[0-9]{1,19}|(?:[a-z0-9](?:[-a-z0-9]{0,61}[a-z0-9])?))",
// "required": true,
// "type": "string"
// },
// "requestId": {
// "description": "An optional request ID to identify requests. Specify a unique request ID so that if you must retry your request, the server will know to ignore the request if it has already been completed.\n\nFor example, consider a situation where you make an initial request and the request times out. If you make the request again with the same request ID, the server can check if original operation with the same request ID was received, and if so, will ignore the second request. This prevents clients from accidentally creating duplicate commitments.\n\nThe request ID must be a valid UUID with the exception that zero UUID is not supported (00000000-0000-0000-0000-000000000000).",
// "location": "query",
// "type": "string"
// },
// "zone": {
// "description": "The name of the zone for this request.",
// "location": "path",
// "pattern": "[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?",
// "required": true,
// "type": "string"
// }
// },
// "path": "{project}/zones/{zone}/disks/{disk}",
// "response": {
// "$ref": "Operation"
// },
// "scopes": [
// "https://www.googleapis.com/auth/cloud-platform",
// "https://www.googleapis.com/auth/compute"
// ]
// }
}

Resources