メインコンテンツまでスキップ

Customize the Extend Service Extension app

Last updated on September 10, 2024
備考

Extend is in Open Beta for AGS Private Cloud Clients! This means that the Extend add-on is available for you to try in your development environment. You can submit your feedback via our Extend Open Beta feedback form.

Overview

An Extend Service Extension app is a RESTful web service created using a stack that includes a gRPC Server and the gRPC Gateway.

In general, the steps to build a RESTful web service from scratch using this stack are as follows:

  1. Define the gRPC server using a protobuf (*.proto) file.
  2. Generate stubs from the *.proto file and implement the gRPC Server.
  3. Generate the gRPC Gateway code from the *.proto file and write an entry point for it.
  4. Generate the OpenAPI 2.0 specification from the *.proto file.

However, with the Service Extension app template, you only need to perform steps 1 and 2. The remaining steps have been packaged to be performed automatically at build time. This app template also comes with a gRPC server interceptor which helps you create RESTful endpoints that require some authorization. Additionally, it comes with built-in instrumentation for observability, ensuring that metrics, traces, and logs are available upon deployment.

This article walks you through how to modify the Extend Service Extension app template and transform it into your own app that fits your requirements.

Prerequisites

You have cloned the Extend Service Extension app template.

git clone https://github.com/AccelByte/extend-service-extension-go

Project structure

Customizing your Extend Service Extension app involves modifying the service.proto and myService.go files. The app initializes key components, such as the gRPC server, in main.go. When a request is made to the RESTful endpoint, the gRPC gateway handles it and forwards it to the corresponding gRPC method. Before myService.go executes any custom logic based on the request, the authServerInterceptor.go first verifies that the request has the necessary access token and authorization. No other files need to be modified unless you require further customization.

.
├── main.go # App starts here
├── pkg
│   ├── common
│   │   ├── authServerInterceptor.go # gRPC server interceptor for access token authentication and authorization
│   │   ├── ...
│   ├── pb # gRPC stubs generated from gRPC server protobuf
│   │   └── ...
│   ├── proto
│   │   ├── service.proto # gRPC server protobuf with additional options for exposing as RESTful web service
│   │   └── ...
│   ├── service
│   │   ├── myService.go # gRPC server implementation containing the custom logic
│   │   └── ...
│   └── ...
└── ...

Modify the protobuf

In the app template, the file can be found in pkg/proto/service.proto.

import "google/api/annotations.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "permission.proto";


service Service {

rpc CreateOrUpdateGuildProgress (CreateOrUpdateGuildProgressRequest) returns (CreateOrUpdateGuildProgressResponse) {
option (permission.action) = CREATE;
option (permission.resource) = "ADMIN:NAMESPACE:{namespace}:CLOUDSAVE:RECORD";
option (google.api.http) = {
post: "/v1/admin/namespace/{namespace}/progress"
body: "*"
};
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
summary: "Update Guild progression"
description: "Update Guild progression if not existed yet will create a new one"
security: {
security_requirement: {
key: "Bearer"
value: {}
}
}
};
}

message CreateOrUpdateGuildProgressRequest {
string namespace = 1;
GuildProgress guild_progress = 2;
}

message CreateOrUpdateGuildProgressResponse {
GuildProgress guild_progress = 1;
}

}

option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
info: {
title: "Service API";
version: "1.0";
};
schemes: HTTP;
schemes: HTTPS;
base_path: "/service";

security_definitions: {
security: {
key: "Bearer";
value: {
type: TYPE_API_KEY;
in: IN_HEADER;
name: "Authorization";
}
}
};
};

The Extend Service Extension service.proto file is essentially a regular gRPC server definition with additional options:

  1. option (google.api.http)

    Describes the relationship between gRPC methods and RESTful endpoints. For more details, refer to the gRPC-Gateway documentation.

  2. option (permission.resource) and option (permission.action)

    Describes the required permission resource and action to be able to invoke each RESTful endpoint. With this, you can create an endpoint that requires a valid AGS access token and permission.

    The permission resource and action values are used by the included gRPC server interceptor to perform authorization.

    • option (permission.resource): You can create your own permission resource string using the AGS format.

    • option (permission.action): Valid values for this option are CREATE, READ, UPDATE, or DELETE. For more details, refer to AGS permission actions.

  3. option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) and option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation)

    Provides information for generating OpenAPI 2.0 specification. For more details, refer to the gRPC-Gateway documentation.

Generate stubs from protobuf

Run this command to generate stubs from the proto file:

make proto    # Generate protobuf code, gateway code, and swagger JSON
important

Always run the above commands after modifying the service.proto file to regenerate the stubs.

Implement request handlers

In the app project, the following can be found in src/master/pkg/service/myService.go.

To set up the service, create a struct that embeds the UnimplementedServiceServer.

import pb "extend-custom-guild-service/pkg/pb"

type MyServiceServerImpl struct {
pb.UnimplementedServiceServer
// Other fields
}

The CreateOrUpdateGuildProgress function is implemented as follows.

func (g MyServiceServerImpl) CreateOrUpdateGuildProgress(
ctx context.Context, req *pb.CreateOrUpdateGuildProgressRequest,
) (*pb.CreateOrUpdateGuildProgressResponse, error) {

// Your implementation

}

Similarly for the GetGuildProgress function.

func (g MyServiceServerImpl) GetGuildProgress(
ctx context.Context, req *pb.GetGuildProgressRequest,
) (*pb.GetGuildProgressResponse, error) {

// Your implementation

}