Giter VIP home page Giter VIP logo

frugal's Introduction

Frugal

Build Status

Frugal is an extension of Apache Thrift which provides additional functionality. Key features include:

  • request headers
  • request multiplexing
  • request interceptors
  • per-request timeouts
  • thread-safe clients
  • code-generated pub/sub APIs
  • support for Go, Java, Dart, and Python (2.7 and 3.5)

Frugal is intended to act as a superset of Thrift, meaning it implements the same functionality as Thrift with some additional features. For a more detailed explanation, see the documentation.

Installation

Homebrew

brew install frugal

Download

Pre-compiled binaries for OS X and Linux are available from the Github releases tab. Currently, adding these binaries is a manual process. If a downloadable release is missing, notify the messaging team to have it added.

If go is already installed and setup you can also simply:

$ go get github.com/Workiva/frugal

From Source

If go is already installed and setup, you can run the following commands:

$ git clone [email protected]:Workiva/frugal.git
$ cd frugal
$ go install

When generating go, be aware the frugal go library and the frugal compiler have separate dependencies.

Usage

Define your Frugal file which contains your pub/sub interface, or scopes, and Thrift definitions.

# event.frugal

// Anything allowed in a .thrift file is allowed in a .frugal file.
struct Event {
    1: i64 ID,
    2: string Message
}

// Scopes are a Frugal extension for pub/sub APIs.
scope Events {
    EventCreated: Event
}

Generate the code with frugal. Currently, only Go, Java, Dart, and Python are supported.

$ frugal -gen=go event.frugal

By default, generated code is placed in a gen-* directory. This code can then be used in your application. Example code is avaliable in the examples/ directory for supported languages.

Prefixes

By default, Frugal publishes messages on the topic <scope>.<operation>. For example, the EventCreated operation in the following Frugal definition would be published on Events.EventCreated:

scope Events {
    EventCreated: Event
}

Custom topic prefixes can be defined on a per-scope basis:

scope Events prefix foo.bar {
    EventCreated: Event
}

As a result, EventCreated would be published on foo.bar.Events.EventCreated.

Prefixes can also define variables which are provided at publish and subscribe time:

scope Events prefix foo.{user} {
    EventCreated: Event
}

This variable is then passed to publish and subscribe calls:

var (
    event = &event.Event{ID: 42, Message: "hello, world!"}
    user  = "bill"
)
publisher.PublishEventCreated(frugal.NewFContext(""), event, user)

subscriber.SubscribeEventCreated(user, func(ctx *frugal.FContext, e *event.Event) {
    fmt.Printf("Received event for %s: %s\n", user, e.Message)
})

Generated Comments

In Thrift, comments of the form /** ... */ are included in generated code. In Frugal, to include comments in generated code, they should be of the form /**@ ... */.

/**@
 * This comment is included in the generated code because
 * it has the @ sign.
 */
struct Foo {}

/**@ This comment is included too. */
service FooService {
    /** This comment isn't included because it doesn't have the @ sign. */
    Foo getFoo()
}

Annotations

Annotations are extra directive in the IDL that can alter the way code is generated. Some common annotations are listed below

Annotation Values Allowed Places Description
vendor Optional location Namespaces, Includes See vendoring includes
deprecated Optional description Service methods, Struct/union/exception fields See deprecating

Vendoring Includes

Frugal does not generate code for includes by default. The -r flag is required to recursively generate includes. If -r is set, Frugal generates the entire IDL tree, including code for includes, in the same output directory (as specified by -out) by default. Since this can cause problems when using a library that uses a Frugal-generated object generated with the same IDL in two or more places, Frugal provides special support for vendoring dependencies through a vendor annotation on includes and namespaces.

The vendor annotation is used on namespace definitions to indicate to any consumers of the IDL where the generated code is vendored so that consumers can generate code that points to it. This cannot be used with * namespaces since it is language-dependent. Consumers then use the vendor annotation on includes they wish to vendor. The value provided on the include-side vendor annotation, if any, is ignored.

When an include is annotated with vendor, Frugal will skip generating the include if use_vendor language option is set since this flag indicates intention to use the vendored code as advertised by the vendor annotation.

If no location is specified by the vendor annotation, the behavior is defined by the language generator.

The vendor annotation is currently only supported by Go, Dart and Java.

The example below illustrates how this works.

bar.frugal ("providing" IDL):

namespace go bar (vendor="github.com/Workiva/my-repo/gen-go/bar")
namespace dart bar (vendor="my-repo/gen-go")
namespace java bar (vendor="com.workiva.bar.custom.pkg")

struct Struct {}

foo.frugal ("consuming" IDL):

include "bar.frugal" (vendor)

service MyService {
    bar.Struct getStruct()
}
frugal -r -gen go:package_prefix=github.com/Workiva/my-other-repo/gen-go,use_vendor foo.frugal

When we run the above command to generate foo.frugal, Frugal will not generate code for bar.frugal since use_vendor is set and the "providing" IDL has a vendor path set for the Go namespace. Instead, the generated code for foo.frugal will reference the vendor path specified in bar.frugal (github.com/Workiva/my-repo/gen-go/bar).

Deprecating

Marks a method or field as deprecated (if supported by the language, or in a comment otherwise), and logs a warning if a deprecated method is called. This is not available on an entire struct, only the fields within the struct.

  Struct GetFooRequest {
      1: String value (deprecated="Use newValue instead")
  }

  GetFooResponse getFoo(10: GetFooRequest request) throws (
    1: FooError error
  ) (deprecated="Use getBar instead")

In Dart, this compiles to

class GetFooRequest implements thrift.TBase {
  ...

  /// Deprecated: Use newValue instead
  @deprecated
  List<String> _value;
  ...
}
  /// Deprecated: Use getBar instead
  @deprecated
  Future<namespace.GetFooResponse> getFoo(frugal.FContext ctx, namespace.GetFooRequest request);

Thrift Parity

Frugal is intended to be a superset of Thrift, meaning valid Thrift should be valid Frugal. File an issue if you discover an inconsistency in compatibility with the IDL.

frugal's People

Contributors

aldenpeterson-wf avatar alishadalal-wk avatar bender-wk avatar blakeroberts-wk avatar brettkail-wk avatar brianshannan-wf avatar charliekump-wf avatar charliestrawn-wf avatar corwinsheahan-wf avatar danielharasymiw-wf avatar dependabot-preview[bot] avatar dependabot[bot] avatar jacobmoss-wf avatar kevinsookocheff-wf avatar natewoods-wf avatar olesiathoms-wk avatar rm-astro-wf avatar rmconsole2-wf avatar rmconsole3-wf avatar rmconsole4-wk avatar rmconsole5-wk avatar rmconsole6-wk avatar rmconsole7-wk avatar robbecker-wf avatar stevenosborne-wf avatar toddbeckman-wf avatar tomdeering-wf avatar tylerrinnan-wf avatar tylertreat-wf avatar yuanmwang-wf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

frugal's Issues

Binary RPCs over json protocol to a Go server return incorrectly

Using a Java or Python client to make binary (the data type) RPCs with protocol=json to a Go server returns incorrect values. For example, the Python client using the FrugalTest.Frugal file from the cross language tests tests binary with:

    binary = "101010"
    print("testBinary({}) = ".format(binary), end="")
    result = yield client.testBinary(ctx, binary)
    if result != binary:
        print("\nUnexpected result ", end="")
        return_code = 1

and returns the following:

testBinary(101010) = 
Unexpected result 10101w

@Workiva/messaging-pp

Generated dart file name does not match import name

DART

L330 in dart/generator.go

Service imports are being converted to all lowercase and causing compilation errors in projects expecting lowerCameCase file names to remain consistent.

Workaround: manually edit the service import to have the correct casing.

Included Files Should Be Located Relative To File Making The Include

Frugal currently searches for included files incorrectly. It searches relative to the location of the initial input file, rather than relative to the location of the file that makes the include.

Example: https://github.com/Workiva/go-linking/pull/460

$ parsimony && frugal --gen go  parsimony_staging/linking_api_v2.frugal
Frugal file not found: parsimony_staging/base_model.frugal

Frugal is dying as it is processing include "base_model.frugal" within parsimony_staging/github.com/tomdeering-wf/messaging-sdk/frugal/base_service.frugal because it is looking for the include relative to parsimony_staging/linking_api_v2.frugal. Expected would be to look for the include relative to parsimony_staging/github.com/tomdeering-wf/messaging-sdk/frugal/base_service.frugal.

@stevenosborne-wf
@tylertreat-wf

Dart TimeoutException currently unwrapped

When a Dart RPC times out the resulting exception is not wrapped in a frugal exception:

50.728800507Z Unhandled exception:
2016-04-22T20:32:50.728811418Z Invalid argument(s): TimeoutException after 0:00:02.000000: Future not completed
2016-04-22T20:32:50.728836424Z #0      _StringBase.+ (dart:core-patch/string_patch.dart:243)
2016-04-22T20:32:50.728860842Z #1      main.<main_async_body>.<anonymous closure> (file:///client/src/dart/largeclient.dart:63:41)
2016-04-22T20:32:50.728896464Z #2      _RootZone.runUnary (dart:async/zone.dart:1137)
2016-04-22T20:32:50.728908382Z #3      _Future._propagateToListeners.handleError (dart:async/future_impl.dart:583)
2016-04-22T20:32:50.728924883Z #4      _Future._propagateToListeners (dart:async/future_impl.dart:641)
2016-04-22T20:32:50.728938421Z #5      _Future._completeError (dart:async/future_impl.dart:432)
2016-04-22T20:32:50.728949751Z #6      _SyncCompleter._completeError (dart:async/future_impl.dart:56)
2016-04-22T20:32:50.728960862Z #7      _Completer.completeError (dart:async/future_impl.dart:27)
2016-04-22T20:32:50.728971738Z #8      FFooClient.blah.<blah_async_body> (package:event/src/f_foo_service.dart:113:3)
2016-04-22T20:32:50.728982903Z #9      _asyncErrorWrapperHelper.<anonymous closure> (dart:async-patch/async_patch.dart:34)
2016-04-22T20:32:50.728994372Z #10     _RootZone.runBinary (dart:async/zone.dart:1142)
2016-04-22T20:32:50.729005018Z #11     _Future._propagateToListeners.handleError (dart:async/future_impl.dart:579)
2016-04-22T20:32:50.729015663Z #12     _Future._propagateToListeners (dart:async/future_impl.dart:641)
2016-04-22T20:32:50.729027598Z #13     _Future._completeError (dart:async/future_impl.dart:432)
2016-04-22T20:32:50.729039546Z #14     _Future.timeout.<anonymous closure> (dart:async/future_impl.dart:689)
2016-04-22T20:32:50.729051163Z #15     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:16)
2016-04-22T20:32:50.729062223Z #16     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:385)
2016-04-22T20:32:50.729072602Z #17     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:414)
2016-04-22T20:32:50.729082904Z #18     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:148)

Timeout errors from Dart should be returned in a frugal error of some sort.

see https://wf-skynet-hrd.appspot.com/results/smithy/539140/1

Frugal incorrectly adds required keyword to arguments

Frugal is adding the required keyword to intermediate thrift files for arguments that do not specify either required or optional. However, I think this is incorrect, since thrift treats this default state differently than required.

http://marconijr.com/posts/thrift-tutorial/

Field Write behavior Read behavior
required always written must read or error
default always written read if present
optional written if set read if present
undefined undefined ignored

old style relative imports (python)

I generated python from directory.frugal and into the --out dir, it made a directory directory.
created init.py in it and then it tries to import directory.f_TargetResolverService as:

import f_TargetResolverService

attempting to import directory raises a NameError:

>>> import directory
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "directory/__init__.py", line 5, in <module>
    import f_DirectoryService
  File "directory/f_DirectoryService.py", line 23, in <module>
    class Iface(f_TargetResolverService.Iface):
NameError: name 'f_TargetResolverService' is not defined

https://github.com/Workiva/frugal/blob/master/compiler/generator/python/generator.go#L1141

Frugal Go generator isn't consistent with how Thrift generates *Args, *Result, and New*

The Go Thrift generator adds an underscore _ to names that end in Args or Result or start with New.

This seems like an unfortunate choice by Thrift, but I don't really know the details. However, the Frugal generator doesn't use the underscored version of the name, which leads to inconsistencies. For example, the following Frugal file:

a.frugal

namespace dart a

struct FooArgs {
    1:string name
}

struct NewBar {
    1:string name
}

struct FooResult {
    1:string name
}

service AService {
    FooResult myFoo(1:FooArgs fa, 2:NewBar nb)
}

... generates f_aservice_service.go with

MyFoo(ctx *frugal.FContext, fa *FooArgs, nb *NewBar) (r *FooResult, err error)

But FooArgs should be FooArgs_, FooResult should be FooResult_, and NewBar should be NewBar_ to be consistent with Thrift.

python generation fails with `--out ./`

I wanted to generate the packages directly to the current working directory in an attempt to work around the lack of prefix. The idea is that it would put them on the python path from which the repo tests could import.

gen_with_frugal Treats Initializer Reference as Include

Generating Go code with gen_with_frugal hits errors when generating for struct fields with initializers:

struct ServiceHealthStatus {
       1: required string version = "1.0",
       2: required HealthCondition status = HealthCondition.PASS,
       3: required string message,
       4: optional map<string,string> metadata,
}
$ frugal -out go/gen --gen go:gen_with_frugal,package_prefix=github.com/Workiva/linking-frugal/go/gen/ parsimony_staging/benchmark.frugal
ERROR unknown Action error: referenced include 'HealthCondition' in constant 'HealthCondition.PASS' not present. See https://github.com/codegangsta/cli/blob/master/CHANGELOG.md#deprecated-cli-app-action-signature

No problem with the definition if we allow Thrift to generate the code.
FYI @brianshannan-wf @tylertreat-wf

Frugal Should Retain Definition Order When Producing Intermediate Thrift

It normally shouldn't matter, but I ran into this gem of a bug with the Go Thrift bindings. We've got a Linking Frugal file that exposes this bug. Thrift generates valid Go code for this file, but Frugal does not.

To avoid issues like this where Thrift and Frugal perform differently on the same input, Frugal ought to preserve the order of definitions when it generates intermediate Thrift files.

@tylertreat-wf
@stevenosborne-wf
FYI
@tannermiller-wf

Go Clients Panic If Used With Unopened Transport

With Frugal 1.19.2, an attempt to use a client constructed with an unopened transport to make an RPC panics (example trace below). It should instead return an error.

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x8 pc=0x6aaaf7]

goroutine 644 [running]:
panic(0xe86d40, 0xc820014080)
	/usr/local/go/src/runtime/panic.go:481 +0x3e6
github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go.(*natsServiceTTransport).Write(0xc8200dc0e0, 0xc8201ee000, 0x1d9, 0x2000, 0x1ffc, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go/stateful_nats_transport.go:332 +0x37
bufio.(*Writer).flush(0xc8203ae0c0, 0x0, 0x0)
	/usr/local/go/src/bufio/bufio.go:562 +0xe0
bufio.(*Writer).Flush(0xc8203ae0c0, 0x0, 0x0)
	/usr/local/go/src/bufio/bufio.go:551 +0x2d
github.com/Workiva/linking-api/vendor/git.apache.org/thrift.git/lib/go/thrift.(*TBufferedTransport).Flush(0xc82052ae60, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/git.apache.org/thrift.git/lib/go/thrift/buffered_transport.go:82 +0x3b
github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go.(*TFramedTransport).Flush(0xc82058b5e0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go/framed_transport.go:131 +0x37d
github.com/Workiva/linking-api/vendor/git.apache.org/thrift.git/lib/go/thrift.(*RichTransport).Flush(0xc8202786f0, 0x0, 0x0)
	<autogenerated>:66 +0x5d
github.com/Workiva/linking-api/vendor/git.apache.org/thrift.git/lib/go/thrift.(*TCompactProtocol).Flush(0xc82058b680, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/git.apache.org/thrift.git/lib/go/thrift/compact_protocol.go:603 +0x4f
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api.(*FBaseServiceClient).ping(0xc820282660, 0xc8204c2de0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api/f_baseservice_service.go:104 +0x425
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api.(*FBaseServiceClient).(github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api.ping)-fm(0xc8204c2de0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api/f_baseservice_service.go:55 +0x38
reflect.Value.call(0xcf8fc0, 0xc820278710, 0x13, 0xfd78c0, 0x4, 0xc820b40000, 0x1, 0x1, 0x0, 0x0, ...)
	/usr/local/go/src/reflect/value.go:435 +0x120d
reflect.Value.Call(0xcf8fc0, 0xc820278710, 0x13, 0xc820b40000, 0x1, 0x1, 0x0, 0x0, 0x0)
	/usr/local/go/src/reflect/value.go:303 +0xb1
github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go.newInvocationHandler.func1(0xf756c0, 0xc820282660, 0x16, 0xfdee98, 0x4, 0x0, 0x0, 0x7fb1f8cee0a8, 0xcf8fc0, 0xcf8fc0, ...)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go/middleware.go:120 +0x1fe
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware.NewIAM.func1.1(0xf756c0, 0xc820282660, 0x16, 0xfdee98, 0x4, 0x0, 0x0, 0x7fb1f8cee0a8, 0xcf8fc0, 0xcf8fc0, ...)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware/iam.go:43 +0x2ce
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware.NewRetry.func1.1.1(0x0, 0x0, 0x0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware/retry.go:57 +0xad
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware.(*RetryConfig).DoWithRetries(0xc820282570, 0xc820b11a30, 0xfdee98, 0x4, 0xc8204c2f00, 0x0, 0x0, 0x0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware/retry.go:72 +0xbb
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware.NewRetry.func1.1(0xf756c0, 0xc820282660, 0x16, 0xfdee98, 0x4, 0x0, 0x0, 0x7fb1f8cee0a8, 0xcf8fc0, 0xcf8fc0, ...)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/middleware/retry.go:59 +0x13c
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/streams/middleware.(*paging).invoke(0xc820544180, 0x0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/streams/middleware/paging.go:125 +0x2ed
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/streams/middleware.NewPaging.func1.1(0xf756c0, 0xc820282660, 0x16, 0xfdee98, 0x4, 0x0, 0x0, 0x7fb1f8cee0a8, 0xcf8fc0, 0xcf8fc0, ...)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/streams/middleware/paging.go:65 +0xdf
github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go.(*Method).Invoke(0xc82017fe30, 0xc82056a920, 0x1, 0x1, 0x0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/frugal/lib/go/middleware.go:67 +0xa5
github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api.(*FBaseServiceClient).Ping(0xc820282660, 0xc8204c2de0, 0x0, 0x0)
	/go/src/github.com/Workiva/linking-api/vendor/github.com/Workiva/linking-frugal/go/gen/workiva_frugal_api/f_baseservice_service.go:69 +0x12c

Generate comments

Thrift will copy comments from the .thrift file to the generated code, which is helpful. It would be helpful to have this in Frugal also.

Allow -r Flag

The recursive flag is supported by Thrift (Frugal does it implicitly). It would be nice for Frugal to support a -r flag rather than erroring out with a bad usage message. This would making it so that I can substitute "frugal" for "thrift" and the same command should "just work".

FYI
@tylertreat-wf
@stevenosborne-wf

Give client control over logging

Frugal's Go lib currently uses the package-global logrus.Logger. This can lead to some verbose logging:

linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QTWY
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QUBM
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QUQA
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QV4Y
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QVJM
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QVYA
null
1
linking-directory
frugal: client heartbeat expired for heartbeat: _INBOX.FD6ZEJC3E01Y63T3M9QWCY
null
1

It would be better if Frugal took a logrus.Logger as an input, probably as a messaging-sdk option. That would allow clients to, for example, filter such warning logs.
@Workiva/messaging-pp

Java: Closing NATS connection may not close all running threads

Reference: nats-io/nats.java#26

This is an issue with the NATS client but pasted here for reference/tracking. The following application never closes:

package com.workiva.messaging_sdk;

import io.nats.client.Connection;
import io.nats.client.ConnectionFactory;
import io.nats.client.Constants;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Main {

  public static void main(String[] args) throws IOException, TimeoutException {
    ConnectionFactory cf = new ConnectionFactory(Constants.DEFAULT_URL);
    Connection nc = cf.createConnection();

    nc.subscribe("foo", m -> {
        System.out.println("Received a message: " + new String(m.getData()));
    });
    nc.close();

    System.out.println("Exiting application");
  }

}

Frugal Dart generates bad import for struct with a CamelCase name

foo.thrift

namespace dart foo_thrift

struct FooThing {
    /** The bar */
    1:string bar,
}

service FooService {
    /** Do the foo */
    void doit(1: FooThing foo)
}

foo.frugal

namespace dart foo_thrift

/** Linking notifications for participating systems */
scope FooNotification {
    /** The system for which this simulated "directed" message is intended. See Thrift constants. */
    prefix "{system}"

    FooCreated: FooThing
}

generates foo_thing.dart and frug_foonotification.dart with bad import:

import 'foothing.dart' as t_foothing;

I'd expect you could just import 'package:foo_thrift/foo_thrift.dart'; instead.

golang: binary fields in union types lacking omitted from "CountSetFields" method

Using the following union:

union Data {
  1:binary WFML,
  2:wcontent.TextDoc Text,
  3:binary DOCX,
  4:binary PDF,
  5:binary XLSX,
  6:wcontent.Spreadsheet Sheet,
  7:wcontent.WdeskFile Wdesk
  8:binary HTML,
  9:wcontent.PaginationModel Primitives,
  10:wcontent.Formdoc Form,
}

Produces the following generated code:

func (p *Data) CountSetFieldsData() int {
        count := 0
        if p.IsSetText() {
                count++
        }
        if p.IsSetSheet() {
                count++
        }
        if p.IsSetWdesk() {
                count++
        }
        if p.IsSetPrimitives() {
                count++
        }
        if p.IsSetForm() {
                count++
        }
        return count
}

This generated code is lacking the binary fields. Looking into the golang generator, I see this code block:

    // Helper function to determine how many optional fields are set in a union
    if s.Type == parser.StructTypeUnion {
        contents += fmt.Sprintf("func (p *%s) CountSetFields%s() int {\n", sName, sName)
        contents += "\tcount := 0\n"
        for _, field := range s.Fields {
            if g.isPointerField(field) {

"g.isPointerField" is determining that it's a base thrift type, and not optional. Maybe with this code:

    switch underlyingType.Name {
    case "binary":
        // According to thrift, these are always like this, not sure why
        return false

We need these fields, or we're unable to transmit binary data.

py:tornado import error

FrugalTest.txt
After generating the attached frugal file (saved in docx for github's sake) with

frugal --gen py:tornado -r --out='test/integration/python/gen_py_tornado' path/to/file/frugalTest.frugal

and attempting to run the attached python file, the following import errors are encountered.

Traceback (most recent call last): File "test/integration/python/server.py", line 10, in <module> from gen_py_tornado.FrugalTest.f_Events_publisher import EventsPublisher File "/Users/jacobmoss/go/src/github.com/Workiva/frugal/test/integration/python/gen_py_tornado/FrugalTest/__init__.py", line 2, in <module> import f_FrugalTest File "/Users/jacobmoss/go/src/github.com/Workiva/frugal/test/integration/python/gen_py_tornado/FrugalTest/f_FrugalTest.py", line 21, in <module> from FrugalTest.FrugalTest import * ImportError: No module named FrugalTest

@charliestrawn-wf @brianshannan-wf and I have discussed the issue.

My current workaround is to add "gen_py_tornado." as a prefix before each "FrugalTest..." import in the generated code.

FrugalTest.txt
server.txt

Invalid Go Code From File With Complex Constants

Frugal currently (52bcb9d) does not generate valid Go code for complex constant initializers, like:
https://github.com/Workiva/go-linking/blob/master/idl/schema/link_schemas.frugal

> frugal -out gen --gen go:package_prefix=github.com/Workiva/linking-notifier/gen/ parsimony_staging/github.com/Workiva/go-linking/link_schemas.frugal
> go build ./...
# github.com/Workiva/linking-notifier/gen/linking_schema
gen/linking_schema/constants.go:23: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:51: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:73: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:89: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:115: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:125: cannot use Schema literal (type *Schema) as type Schema in map value

Using Thrift 0.9.3 directly on this file generates valid Go code.

Python and Golang treating enum's differently

The generated type code handles enums between go and python differently.

Steps to reproduce:
Start nats
Start go natsServer frugal example
Run go natsClient frugal example
Run python.asyncio nats_client.py frugal example
Result: The track Pro value will be 1 on python client and "ASCAP" on go client

I have not tried Java and Dart to see how they handle this.

Handle generating a constant from an enum

Given the following definition:

enum Numberz
{
  ONE = 1,
  TWO,
  THREE,
  FIVE = 5,
  SIX,
  EIGHT = 8
}

const Numberz myNumberz = Numberz.ONE;

Frugal will throw "panic: interface conversion: interface is parser.Identifier, not []parser.KeyValue" when trying to generate a constant from an enum.

binary fields not deserialized correctly when using gen_with_frugal

If a frugal model contains a binary field, it does not deserialize the bytes correctly.

Example frugal:

namespace go breakfrugal

struct BreakGenWithFrugal {
    3: binary data,
}

Example code that shows this reproducing:

package main

import (
    "fmt"

    "git.apache.org/thrift.git/lib/go/thrift"

    "github.com/Workiva/break_frugal/gen-go/breakfrugal"
)

func main() {
    testObj := breakfrugal.NewBreakGenWithFrugal()
    testObj.Data = []byte{10, 11, 12}
    fmt.Printf("Data len before serialize/deserialize: %d\n", len(testObj.Data))

    serializer := thrift.NewTSerializer()
    serializer.Protocol = thrift.NewTCompactProtocol(serializer.Transport)
    testObjBytes, _ := serializer.Write(testObj)

    desielizedTestObj := breakfrugal.NewBreakGenWithFrugal()
    deserializer := thrift.NewTDeserializer()
    deserializer.Protocol = thrift.NewTCompactProtocol(deserializer.Transport)
    deserializer.Read(desielizedTestObj, testObjBytes)

    fmt.Printf("Data len after deserializing: %d\n", len(desielizedTestObj.Data))
}

Hint: copy those files to $GOPATH/Workiva/break_frugal

Commands showing problem:
Working as expected (no gen_with_frugal):

rm -Rf gen-go/; and frugal -gen=go break_frugal.frugal; go run break.go
Consider using the "gen_with_frugal" language option to have Frugal generate code in place of Thrift.
This is an experimental feature. Please file a GitHub issue if you encounter problems.
Data len before serialize/deserialize: 3
Data len after deserializing: 3

Broken (using gen_with_frugal):

rm -Rf gen-go/; and frugal -gen=go:gen_with_frugal=true break_frugal.frugal; go run break.go
Data len before serialize/deserialize: 3
Data len after deserializing: 0

@brianshannan-wf - tagging you as per request from Steven Osborne

Generate Publisher interface type, not just struct for Go.

In Go, a frugal definition like:

scope Events {
    EventCreated: Event
}

generates a Publisher & Subscriber similar to:

type EventsPublisher struct {
    ...
}

func (e *EventsPublisher) PublishEventsCreated(...) {
    ...
}

type EventsSubscriber struct {
    ...
}

func (e *EventsSubscriber) SubscribeEventsCreated(...) {
    ...
}

I propose that instead it creates interface types with default implementations:

type EventsPublisher interface {
    PublishEventsCreated(...) 
}

type eventsPublisher struct {
    ...
}

func (e *eventsPublisher) PublishEventsCreated(...) {
    ...
}

type EventsSubscriber interface {
    SubscribeEventsCreated(...)
}

type eventsSubscriber struct {
    ...
}

func (e *eventsSubscriber) SubscribeEventsCreated(...) {
    ...
}

This would drastically improve mocking of those types in unit tests.

Frugal allows fields/structs to be named with reserved keywords

@grahamcummins-wf FYI, copying this into a GH issue for better documentation and discussion


[6:23 PM] Graham Cummins: Looks like frugal (at least in 1.14) has some issues with reserved Java symbols that are not reserved thrift symbols.
[6:24 PM] Graham Cummins: Particularly, this struct passes through frugal generate without errors
[6:24 PM] Graham Cummins:
union Modification {
1: VertexCreate new,
2: string insert,
3: string copy,
4: string move,
5: bool server,
6: bool destroy,
7: map<string, Value> setProperties
}
[6:24 PM] Graham Cummins: but it generates this Java
[6:24 PM] Graham Cummins:
case NEW:
if (field.type == NEW_FIELD_DESC.type) {
VertexCreate new = new VertexCreate();
new.read(iprot);
return new;
[6:24 PM] Graham Cummins: Java doesn't like "VertexCreate new = new VertexCreate();" much
[6:31 PM] Graham Cummins: checking if that's still an issue on latest
[6:33 PM] Graham Cummins: Yep, that's still there in 1.19.2
---- Thursday October 13, 2016 ----
[8:12 AM] Brian Shannan: @GrahamCummins what do you think would be the best solution to that? Validating none of the names/types are language keywords and erroring out? Something else?
[9:21 AM] Graham Cummins: Short term, I'd say just documentation. It would be good to know what symbols can't be used.
I don't think top-level keywords can be supported reasonably. Some of those uses in the gen sources could be translated to a different symbol, but eventually the user would expect to be able to write "Modification.new(...)", which isn't legal in Java.
[9:21 AM] Graham Cummins: I think the best solution would be for the Frugal compiler to throw an error declaring the IDL invalid (since it can't be translated to at least one supported language)
[9:23 AM] Nate Woods: On the same subject: "Error" is an invalid exception name in Dart.
[9:24 AM] Graham Cummins: I can fix my IDL by calling that thing "create" instead of "new". That's no problem. The problems are that:

  1. The error that shows up is obscure, buried in the generated sources, and occurs too late - after the generator has completed successfully, when I go to use the sources.
  2. Our API repo builds and handles the Java sources at PR time, so it caught this, but it doesn't build and try to use all the other languages. I'm worried that there might be some other special symbols that work in Java, but fail in Dart or Go, and that I won't know about those until some Java code is using the API, and another user comes along.
    [9:32 AM] Graham Cummins: On this topic, some time ago it was the case that recursively defined types, which are legal in the IDL, and work fine in Java (and Dart), generate circular import errors in Python. I'm not sure if that's been fixed, but if it hasn't then some of our deployed API can't be used from Python (because it was designed around a recursive type). If we want to ensure that we can add clients across the platform, we'll need to know up front when an IDL won't work in language X. That either means Frugal building in the knowledge and throwing errors (or at least warnings), or it means every API repo autobuilding all Workiva languages at PR time and testing all the generated code

Incorrect field modifiers in intermediate Thrift

Currently, we generate the intermediate Thrift assuming the default field modifier (no modifier) is equivalent to required. This is actually not the case.

Field Write behavior Read behavior
required always written must read or error
default always written read if present
optional written if set read if present
undefined undefined ignored

Frugal Should Retain Global Definition Order When Producing Intermediate Thrift

Otherwise we get cases where Thrift can handle some files and Frugal cannot. For example, https://github.com/Workiva/go-linking/blob/master/idl/schema/link_schemas.frugal

> frugal -out gen --gen go:package_prefix=github.com/Workiva/linking-notifier/gen/ parsimony_staging/github.com/Workiva/go-linking/link_schemas.frugal
> go build ./...
# github.com/Workiva/linking-notifier/gen/linking_schema
gen/linking_schema/constants.go:23: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:51: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:73: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:89: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:115: cannot use Schema literal (type *Schema) as type Schema in map value
gen/linking_schema/constants.go:125: cannot use Schema literal (type *Schema) as type Schema in map value

Using Thrift 0.9.3 directly on this file generates valid Go code.

frugal_superset not generating code for includes

In my frugal file, I have the following (file does exist):
include "github.com/tomdeering-wf/messaging-sdk/base.thrift"

Running the following command:

frugal -gen=dart -out=sdk/dart/gen -file=parsimony_staging/github.com/Workiva/wLayout/wtranslate-rpc.frugal

Exits with code 0 for success.

However, the generated output contains no definition for the include and its objects.

Interestingly, if I delete the file on disk that the include refers to, frugal errors out as expected... so it seems to notice the file, but skips generation.

Example:

frugal -gen=dart -out=sdk/dart/gen -file=parsimony_staging/github.com/Workiva/wLayout/wtranslate-rpc.frugal                     Fri Nov 20 14:50:53 MST 2015
[ERROR:/Users/justinlindh/go/src/github.com/Workiva/wTranslate/parsimony_staging/github.com/Workiva/wLayout/wtranslate-rpc.thrift:39] (last token was 'base.BaseService')
Service "base.BaseService" has not been defined.
[WARNING:/Users/justinlindh/go/src/github.com/Workiva/wTranslate/parsimony_staging/github.com/Workiva/wLayout/wtranslate-rpc.thrift:4] Could not find include file github.com/tomdeering-wf/messaging-sdk/basemunged.thrift

graph_api fails to build - cannot find validate()

I am trying to compile graph_api with the latest frugal, and get the following error during the java compilation:

[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/Branch.java:[378,32] cannot find symbol
  symbol:   method validate()
  location: variable version of type com.workiva.graph.thrift.GraphVersion
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/PropertySet.java:[461,30] cannot find symbol
  symbol:   method validate()
  location: variable value of type com.workiva.graph.thrift.Value
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/Property.java:[807,29] cannot find symbol
  symbol:   method validate()
  location: variable type of type com.workiva.graph.thrift.Value
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/frugal/api/spreadsheet/Format.java:[461,29] cannot find symbol
  symbol:   method validate()
  location: variable tags of type com.workiva.frugal.api.spreadsheet.Tags
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/StructuredQuery.java:[767,31] cannot find symbol
  symbol:   method validate()
  location: variable source of type com.workiva.graph.thrift.TraversalSource
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/DataPoint.java:[461,30] cannot find symbol
  symbol:   method validate()
  location: variable value of type com.workiva.graph.thrift.Value
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/PropertyHistoryValue.java:[459,30] cannot find symbol
  symbol:   method validate()
  location: variable value of type com.workiva.graph.thrift.Value
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/PropertyValueFilter.java:[461,31] cannot find symbol
  symbol:   method validate()
  location: variable target of type com.workiva.graph.thrift.Value
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/GraphService.java:[4539,32] cannot find symbol
  symbol:   method validate()
  location: variable version of type com.workiva.graph.thrift.GraphVersion
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/GraphService.java:[4542,30] cannot find symbol
  symbol:   method validate()
  location: variable query of type com.workiva.graph.thrift.Query
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/GraphService.java:[5438,32] cannot find symbol
  symbol:   method validate()
  location: variable version of type com.workiva.graph.thrift.GraphVersion
[ERROR] /Users/davidgianforte/workiva/graph_api/gen/java/com/workiva/graph/thrift/GraphService.java:[6364,32] cannot find symbol
  symbol:   method validate()
  location: variable version of type com.workiva.graph.thrift.GraphVersion
[INFO] 12 errors
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.920s
[INFO] Finished at: Fri Aug 12 11:32:23 MDT 2016

To reproduce you will need parismony and latest frugal, clone graph_api and run make package.

Compilation works with "gen_with_frugal=false".

gen_with_frugal Adds typedef, Fixes Thrift Go Binding Bug

IDL: list<JobID> cancelJobs(1:list<JobID> jobs) throws (1:BenchmarkError bErr)
Thrift: CancelJobs(*frugal.FContext, []int64) ([]int64, error)
Frugal: CancelJobs(*frugal.FContext, []linking_benchmark.JobID) ([]linking_benchmark.JobID, error)

In other words, gen_with_frugal is incompatible with the Thrift bug. I think we want it to generate compatible interfaces, correct?

@tylertreat-wf @brianshannan-wf

Aliasing includes

It would be nice to be able to alias includes or "promote" them to the current namespace.

Before:

include "../base_models.frugal"
include "linking_v2_models.frugal"

struct MyStruct {
    1: base_models.SomeModel model,
    2: linking_v2_models.Link link,
}

After:

include "../base_models.frugal" as "base"
include "../linking_v2_models.frugal" as "." // Promote to current namespace

struct MyStruct {
    1: base.SomeModel model,
    2: Link link,
}

Frugal does not handle typedef typed as another typedef

Thrift allows this, but Frugal does not.

typedef string Foo

typedef Foo Foo2

struct Bar {
  1: Foo2 f,
}

With Frugal:

frugal -r --gen dart:library_prefix=a a.frugal
Generating Thrift code with Frugal. If you encounter problems, file a GitHub issue and generate your
code with "gen_with_frugal=false" to use the Thrift compiler instead.
Failed to generate a.frugal:
        not a valid thrift type: Foo

With Thrift:

(scratch)frugal$ thrift -r --gen dart:library_prefix=a a.frugal
(scratch)frugal$

'exception' handling difference with thrift

If gen_with_frugal=false, then an idl with a structure or union property named exception will cause frugal to fail with an error. With gen_with_frugal=true, it doesn't cause and error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.