Liking cljdoc? Tell your friends :D

Clojure

A Clojure library designed to easily encode and decode protobuf messages by using Clojure maps.

Installation

Add the below dependency to your project.clj file:

           [io.odpf/stencil-clj "0.2.1"]

Usage

Consider following proto message

syntax = "proto3";

package example;

option java_multiple_files = true;
option java_package = "io.odpf.CljTest";

message Address {
	string city = 1;
	string street = 2;
}

message Person {
	enum Gender {
		UNKNOWN = 0;
		MALE = 1;
		FEMALE = 2;
		NON_BINARY = 3;
	}
	string name = 1;
	Address address = 2;
	Gender gender = 3;
	repeated string email_list = 4;
	int32 age = 5;
}
  1. Create stencil client. You can refer to java client documentation for all available options.
(ns test
  (:require [stencil.core :refer [create-client]]))

(def client (create-client {:url "<stencil service url>"
                :refresh-cache true
                :refresh-strategy :version-based-refresh
                :headers {"<headerkey>" "<header value>"}))
  1. To serialize data from clojure map
(:require [stencil.core :refer [serialize]])

(def serialized-data
     (serialize client "io.odpf.CljTest" {:name "Foo"
                                          :address {:street "bar"}
                                          :email-list ["a@example.com" "b@b.com"]
                                          :gender :NON-BINARY
                                          :age 10}))
  1. Deserialize data from bytes to clojure map
(:require [stencil.core :refer [deserialize]])

(deserialize client "io.odpf.CljTest" serialized-data)
;; prints
;; {:name "Foo"
;; :address {:street "bar"}
;; :email-list ["a@example.com" "b@b.com"]
;; :gender :NON-BINARY
;; :age 10}

Protocol buffers - Clojure interop

ProtobufClojureNotes
field nameskeywords in kebab casename -> :name, field_name -> :field-name
scalar fieldsValues follow protobuf-java scalar value mappings
enumsValues converted as keywords of enum's original valueUNKNOWN -> :UNKNOWN
messagesclojure mapmessage Hello {string name = 1;} -> {:name "odpf"}
repeated fieldsclojure vector
one-of fieldstreated as regular fieldsif two fields are set that are part of one-of, last seen value is considered while serializing data
mapmap values follow it's wire representationfor map<string, string> type, example value will be [{:key "key" :value "value"}]

Note on errors: Serialize will throw error in following cases

  1. unknown field is passed that's not present in schema {:cause :unknown-field :info {:field-name <field-name>}}
  2. if non-collection type is passed to repeated field {:cause :not-a-collection :info {:value <value>}}
  3. If unknown enum value passed that's not present in schema {:cause :unknown-enum-value :info {:field-name <field-name>}}

API

  • create-client (client-config)

    Returns a new Stencil Clojure client instance by passing client-config.

    Client config structure :

    KeyTypeDescription
    urlStringStencil url to fetch latest descriptor sets
    refresh-cacheBooleanWhether the cache should be refreshed or not
    refresh-ttlIntegerCache TTL in minutes
    request-timeoutIntegerRequest timeout in milliseconds
    request-backoff-timeIntegerRequest back off time in minutes
    retry-countIntegerNumber of retries to be made to fetch descriptor sets
    headersMapMap with key as header key and value as header value, which will be passed to stencil server
    refresh-strategykeywordPossible values :version-based-refresh, :long-polling-refresh. Default :long-polling-refresh

    Example:

     (let [sample-client-config {:url       "https://example-url"
                                :refresh-cache        true
                                :refresh-ttl          100
                                :request-timeout      10000
                                :request-backoff-time 100
                                :retry-count          3
                                :headers              {"Authorization" "Bearer <token>"}
                                :refresh-strategy     :version-based-refresh
                                }]
           (create-client sample-client-config))
    
  • get-descriptor (client proto-class-name)

    Returns protobuf descriptor object for the given protobuf class name.

    Argument list :

    KeyTypeDescription
    clientObjectInstantiated Clojure client object
    proto-class-nameStringName of the proto class whose proto descriptor object is required

    Response structure

    ValueTypeDescription
    proto-descObjectProtobuf descriptor for given proto class name

    Example:

    (let [client (create-client sample-client-config)
          proto-package "io.odpf.stencil_clj_test"
          proto-class-name "Scalar"
          fully-qualified-proto-name (str proto-package "." proto-class-name)]
        (get-descriptor client fully-qualified-proto-name))
    
  • deserialize (client proto-class-name data)

    Returns Clojure map for the given protobuf encoded byte array and protobuf class name.

    Argument list :

    KeyTypeDescription
    clientObjectInstantiated Clojure client object
    proto-class-nameStringName of the proto class whose proto descriptor object is required
    dataByte-ArrayData (byte-array) to be deserialized using proto-descriptor object

    Response structure

    ValueTypeDescription
    deserialized-messagePersistentArrayMapDeserialized message (Clojure Map)

    Example:

    (let [client (create-client sample-client-config)
          proto-package "io.odpf.stencil_clj_test"
          proto-class-name "Scalar"
          fully-qualified-proto-name (str proto-package "." proto-class-name)
          proto-desc (get-descriptor client fully-qualified-proto-name)
          data-to-deserialize (serialize client fully-qualified-proto-name{:field-one 1.25})]
         (deserialize client fully-qualified-proto-name data-to-deserialize))
    
  • serialize (client proto-class-name map)

    Returns protobuf encoded byte array for the given Clojure and protobuf class name.

    Argument list :

    KeyTypeDescription
    clientObjectInstantiated Clojure client object
    proto-class-nameStringName of the proto class whose proto descriptor object is required
    mapPersistentArrayMapData (in the form of map) to be serialized using proto descriptor object

    Response structure

    ValueTypeDescription
    serialized-messageByte-ArraySerialized message (byte-array)

    Example:

    (let [client (create-client sample-client-config)
          proto-package "io.odpf.stencil_clj_test"
          proto-class-name "Scalar"
          fully-qualified-proto-name (str proto-package "." proto-class-name)
          proto-desc (get-descriptor client fully-qualified-proto-name)]
         (serialize client fully-qualified-proto-name {:field-one 1.25}))
    

Development

  • Ensure leiningen is installed.

  • Run tests: lein clean && lein javac && lein test

  • Run formatting: lein cljfmt fix

Can you improve this documentation? These fine people already did:
Harikrishna, Hari krishna & Vibhu Garg
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close