@@ -24,11 +24,12 @@ type ClientCallbacks struct {
2424 OnAck func (ackId uint64 ) error
2525}
2626
27+ // Client is a client for communicating over STEF/gRPC protocol.
2728type Client struct {
2829 grpcClient stef_proto.STEFDestinationClient
2930 stream stef_proto.STEFDestination_StreamClient
3031 callbacks ClientCallbacks
31- clientSchema * schema. WireSchema
32+ clientSchema ClientSchema
3233 logger types.Logger
3334
3435 // Running state
@@ -84,15 +85,47 @@ func (w *grpcWriter) WriteChunk(header []byte, content []byte) error {
8485
8586var ErrServerInvalidResponse = errors .New ("invalid server response" )
8687
88+ // ClientSettings contains configuration settings for creating a Client.
8789type ClientSettings struct {
90+ // Logger instance used for logging client operations.
8891 Logger types.Logger
92+
8993 // gRPC stream to send data over.
90- GrpcClient stef_proto.STEFDestinationClient
91- ClientSchema * schema.WireSchema
92- Callbacks ClientCallbacks
94+ GrpcClient stef_proto.STEFDestinationClient
95+
96+ // ClientSchema of the client.
97+ ClientSchema ClientSchema
98+
99+ // Callbacks for handling events such as acknowledgments and disconnections.
100+ Callbacks ClientCallbacks
93101}
94102
95- func NewClient (settings ClientSettings ) * Client {
103+ type ClientSchema struct {
104+ // The name of the root struct of the schema.
105+ RootStructName string
106+
107+ // The wire schema of the client.
108+ WireSchema * schema.WireSchema
109+ }
110+
111+ // NewClient creates a new instance of the Client.
112+ //
113+ // Requirements:
114+ // - The `RootStructName` in `ClientSchema` must not be empty.
115+ // - The `WireSchema` in `ClientSchema` must not be nil.
116+ //
117+ // Example:
118+ //
119+ // clientSettings := stefgrpc.ClientSettings{
120+ // GrpcClient: grpcClient,
121+ // ClientSchema: stefgrpc.ClientSchema{RootStructName: "Metrics", WireSchema: &schema},
122+ // Callbacks: stefgrpc.ClientCallbacks{},
123+ // }
124+ // client, err := stefgrpc.NewClient(clientSettings)
125+ // if err != nil {
126+ // log.Fatalf("Failed to create client: %v", err)
127+ // }
128+ func NewClient (settings ClientSettings ) (* Client , error ) {
96129 if settings .Logger == nil {
97130 settings .Logger = internal.NopLogger {}
98131 }
@@ -101,6 +134,13 @@ func NewClient(settings ClientSettings) *Client {
101134 settings .Callbacks .OnDisconnect = func (err error ) {}
102135 }
103136
137+ if settings .ClientSchema .RootStructName == "" {
138+ return nil , fmt .Errorf ("client schema root struct name is empty" )
139+ }
140+ if settings .ClientSchema .WireSchema == nil {
141+ return nil , fmt .Errorf ("client schema wire schema is nil" )
142+ }
143+
104144 client := & Client {
105145 grpcClient : settings .GrpcClient ,
106146 callbacks : settings .Callbacks ,
@@ -109,7 +149,7 @@ func NewClient(settings ClientSettings) *Client {
109149 waitCh : make (chan struct {}),
110150 }
111151
112- return client
152+ return client , nil
113153}
114154
115155func (c * Client ) Connect (ctx context.Context ) (pkg.ChunkWriter , pkg.WriterOptions , error ) {
@@ -137,6 +177,19 @@ func (c *Client) Connect(ctx context.Context) (pkg.ChunkWriter, pkg.WriterOption
137177 }
138178 defer closeOnErr ()
139179
180+ // Send the first message to the server, include the root struct name.
181+ clientMsg := & stef_proto.STEFClientFirstMessage {
182+ RootStructName : c .clientSchema .RootStructName ,
183+ }
184+ err = stream .Send (
185+ & stef_proto.STEFClientMessage {
186+ FirstMessage : clientMsg ,
187+ },
188+ )
189+ if err != nil {
190+ return nil , opts , fmt .Errorf ("failed to send to server: %w" , err )
191+ }
192+
140193 // The server must send capabilities message.
141194 message , err := stream .Recv ()
142195 if err != nil {
@@ -162,7 +215,7 @@ func (c *Client) Connect(ctx context.Context) (pkg.ChunkWriter, pkg.WriterOption
162215 }
163216
164217 // Check if server schema is backward compatible with client schema.
165- compatibility , err := serverSchema .Compatible (c .clientSchema )
218+ compatibility , err := serverSchema .Compatible (c .clientSchema . WireSchema )
166219 switch compatibility {
167220 case schema .CompatibilityExact :
168221 // Schemas match exactly, nothing else is needed, can start sending data.
@@ -171,12 +224,12 @@ func (c *Client) Connect(ctx context.Context) (pkg.ChunkWriter, pkg.WriterOption
171224 // ServerStream schema is superset of client schema. The client MUST specify its schema
172225 // in the STEF header.
173226 opts .IncludeDescriptor = true
174- opts .Schema = c .clientSchema
227+ opts .Schema = c .clientSchema . WireSchema
175228
176229 case schema .CompatibilityIncompatible :
177230 // It is neither exact match nor is server schema a superset, but server schema maybe subset.
178231 // Check the opposite direction: if client schema is backward compatible with server schema.
179- compatibility , err = serverSchema .Compatible (c .clientSchema )
232+ compatibility , err = serverSchema .Compatible (c .clientSchema . WireSchema )
180233
181234 if err != nil || compatibility == schema .CompatibilityIncompatible {
182235 return nil , opts , fmt .Errorf ("client and server schemas are incompatble: %w" , err )
@@ -185,7 +238,7 @@ func (c *Client) Connect(ctx context.Context) (pkg.ChunkWriter, pkg.WriterOption
185238 if compatibility == schema .CompatibilitySuperset {
186239 // Client schema is superset of server schema. The client MUST downgrade its schema.
187240 opts .IncludeDescriptor = true
188- opts .Schema = c .clientSchema
241+ opts .Schema = c .clientSchema . WireSchema
189242 }
190243 }
191244
0 commit comments