Basic Client#

This guide provides a step-by-step walkthrough for setting up a basic client to interact with the Fleet API. Follow the instructions to authenticate and connect to the API in order to make requests. Some general gRPC and protobuf knowledge are recommended for working with this example.

Project setup#

First download the Fleet State API proto file from here: fleet_state.proto

mkdir fleetstate-client-go && cd fleetstate-client-go
cp your-downloads/fleet_state.proto .

Generate the go code for the supplied proto file using protoc or buf.

Note

We will use the statev1beta1 package name as output directory for our generated code. Make sure to replace this with the current version of the proto file.

Code Generation with buf

Download the buf configuration files for generating code:

cp your-downloads/buf.yaml your-downloads/buf.gen.yaml .
buf mod update
buf generate
Code Generation with protoc
protoc --go_out=./fleetstatev1beta1 --go_opt=paths=source_relative \
    --go-grpc_out=./fleetstatev1beta1 --go-grpc_opt=paths=source_relative \
    -I . 
    fleet_state.proto

Finalize the code generation by creating a go module.

go mod init moia.io/fleetstate-client
go mod tidy

Create our client file. We can use go run to quickly run our go code.

touch client.go
go run client.go

We will use some dependencies during this tutorial, depending on your setup you might need to repeatedly run go mod tidy or go get in order to install them.

Requires Node.js 12 or higher.

We will use the ts-proto and nice-grpc libraries to generate a TypeScript client for the FleetState API with either protoc or buf. The commands below will set up a new project and install the required dependencies. Please also make sure to copy the protobuf definitions of the Fleet API to the fleetstate-client-ts directory (last command). The protobuf definitions can be found here fleet_state.proto.

mkdir fleetstate-client-ts && cd fleetstate-client-ts
brew install protobuf
cp your-donwloads/fleet_state.proto .
Code Generation with buf

Download the buf configuration files for generating code:

cp your-downloads/buf.yaml your-downloads/buf.gen.yaml .
npm install @bufbuild/buf
npx buf mod update
npx buf generate
Code Generation with protoc

Run protoc with the TypeScript plugin to generate the client code. Checkout ts-proto for more information on the options used below.

npm init -y
npm install nice-grpc ts-proto
protoc \
--plugin=./node_modules/.bin/protoc-gen-ts_proto \
- I . \
--ts_proto_out=. \
--ts_proto_opt=outputServices=generic-definitions \
--ts_proto_opt=outputServices=nice-grpc \
--ts_proto_opt=esModuleInterop=true \
--ts_proto_opt=useExactTypes=false \
./fleet_state.proto 

We then create our client file and can use ts-node to quickly run our TypeScript code.

touch client.ts
npx ts-node client.ts

File structure#

package main

import (
 context "context"
 "log"
 "net/http"
 "time"

 "golang.org/x/oauth2"
 "golang.org/x/oauth2/clientcredentials"
 grpc "google.golang.org/grpc"
 "google.golang.org/grpc/credentials"
 "google.golang.org/grpc/credentials/oauth"
 "google.golang.org/protobuf/encoding/protojson"
 "moia.io/fleetstate-client/fleetstatev1beta1"
)

func main() {
    // The code of the next steps goes here
}
import * as grpc from "nice-grpc";
import { FleetStateServiceDefinition } from "./fleetstate";

async function main() {
  // The code of the next steps goes here
}

Authenticate#

First you need to obtain a token from the API gateway. To do this we will use the golang.org/x/oauth2 library. You can find more information about authentication here. You can also use an OAuth2 client, as long as you authenticate with the client_credentials grant type and use the authorization header with the Basic scheme.

clientId := os.Getenv("CLIENT_ID")
clientSecret := os.Getenv("CLIENT_SECRET")
const tokenUrl = "https://fleet-api.trip.int.moia-group.io/" +
	"auth/oauth/token"
const testVehicleId = "819f0cda-62dd-4251-9efd-9c4294b4f42d"

oauthConfig := clientcredentials.Config{
	ClientID:     clientId,
	ClientSecret: clientSecret,
	TokenURL:     tokenUrl,
	AuthStyle:    oauth2.AuthStyleInHeader,
}

httpCtx := context.Background()
httpClient := &http.Client{Timeout: 30 * time.Second}
httpCtx = context.WithValue(httpCtx, oauth2.HTTPClient, httpClient)

// Used in the next steps
accessToken, err := oauthConfig.Token(httpCtx)

if err != nil {
	log.Fatalf("could not fetch token: %v", err)
}

First you need to obtain a token from the API gateway. To do this we will send an HTTP request to the tokenURL using the fetch() API. You can find more information about authentication here. You can also use an OAuth2 client, as long as you authenticate with the grant type client_credentials and use the authorization header with the Basic scheme.

const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const tokenUrl =
    "https://fleet-api.trip.int.moia-group.io/" +
    "auth/oauth/token";
const testVehicleId = "819f0cda-62dd-4251-9efd-9c4294b4f42d";

const authHeader = Buffer.from(`${clientId}:${clientSecret}`).toString(
    "base64"
);

const http2AuthClient = http2.connect(tokenUrl);

const tokenRequest = http2AuthClient.request({
    ":method": "POST",
    ":path": "/auth/oauth/token",
    "authorization": `Basic ${authHeader}`,
    "content-type": "application/x-www-form-urlencoded",
});

tokenRequest.setEncoding("utf8");

const requestBody = "grant_type=client_credentials";

tokenRequest.write(requestBody);
tokenRequest.end();

let tokenResponse = "";

for await (const data of tokenRequest) {
    tokenResponse += data;
}

const parsedTokenResponse = JSON.parse(tokenResponse) as Record<string, any>;

if (parsedTokenResponse.error) {
    console.error(parsedTokenResponse.error_description);
    return;
}

http2AuthClient.close();

// Used in the next steps
const accessToken = parsedTokenResponse.access_token;

Connect to the API#

Setup SSL credentials for transport and supply our token with every request using grpc.WithPerRPCCredentials.

perRPC := oauth.TokenSource{
	TokenSource: oauth2.StaticTokenSource(accessToken),
}

const apiUrl = "fleet-api.trip.int.moia-group.io:443"

conn, err := grpc.Dial(
	apiUrl,
	grpc.WithTransportCredentials(
		credentials.NewClientTLSFromCert(nil, ""),
	),
	grpc.WithPerRPCCredentials(perRPC),
)
if err != nil {
	log.Fatalf("could not connect to %s: %v", apiUrl, err)
}
defer conn.Close()

fleetStateClient := fleetstatev1beta4.NewFleetStateServiceClient(conn)

ctx, cancel := context.WithTimeout(context.Background(), unaryTimeout)
defer cancel()

// Used for printing responses
jsonMarshalOptions := protojson.MarshalOptions{
	Multiline:       true,
	EmitUnpopulated: true,
}

We use nice-grpc to set up a gRPC channel and client. We use the grpc.Metadata class to add the Authorization header to all of our gRPC requests.

const apiUrl = "fleet-api.trip.int.moia-group.io:443";

const clientFactory = grpc.createClientFactory().use(deadlineMiddleware);

const channel = grpc.createChannel(
    apiUrl,
    grpc.ChannelCredentials.createSsl()
);

const client = clientFactory.create(FleetStateServiceDefinition, channel, {
    "*": {
        metadata: new grpc.Metadata({
            Authorization: `Bearer ${accessToken}`,
        }),
    },
});

Continue with the API Usage example where we will call some of the Fleet API methods.