Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tatethurston/TwirpScript
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.0.45
Choose a base ref
...
head repository: tatethurston/TwirpScript
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v0.0.46
Choose a head ref
  • 5 commits
  • 42 files changed
  • 2 contributors

Commits on Feb 16, 2022

  1. remove naming suffixes (#125)

    Removes 'Service' suffix from generated service types. Removes 'Handler'
    suffix from 'create<Service>' helper.
    
    This is a breaking change.
    
    This enables better out of the box integration with `buf` which expects
    service names to end with the Serice suffix. When following that
    recommendation, TwirpScript would generate 'FooServiceService'.
    
    Co-authored-by: Tate <[email protected]>
    tatethurston and Tate authored Feb 16, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    154ae7e View commit details
  2. add json serialization to readme (#126)

    Co-authored-by: Tate <[email protected]>
    tatethurston and Tate authored Feb 16, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5fd9292 View commit details
  3. add exclude option to twirp.json (#127)

    * add exclude option to twirp.json
    
    fixes 113.
    
    * Update README.md
    
    Co-authored-by: Tate <[email protected]>
    tatethurston and Tate authored Feb 16, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    626464e View commit details
  4. add null to optional ts types (#128)

    Co-authored-by: Tate <[email protected]>
    tatethurston and Tate authored Feb 16, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8d57a00 View commit details
  5. v0.0.46 (#129)

    Co-authored-by: Tate <[email protected]>
    tatethurston and Tate authored Feb 16, 2022

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a4d83f9 View commit details
Showing with 5,325 additions and 225 deletions.
  1. +32 −0 CHANGELOG.md
  2. +99 −29 README.md
  3. +3 −5 clientcompat/src/clientcompat.pb.ts
  4. +1 −1 examples/authentication/src/client/index.tsx
  5. +4 −4 examples/authentication/src/protos/authentication.pb.ts
  6. +3 −5 examples/authentication/src/protos/haberdasher.pb.ts
  7. +3 −3 examples/authentication/src/server/index.ts
  8. +2 −2 examples/authentication/src/server/middleware/requireAuthentication.ts
  9. +5 −6 examples/authentication/src/server/services/authentication/index.ts
  10. +3 −6 examples/authentication/src/server/services/haberdasher/index.ts
  11. +2 −2 examples/authentication/src/server/services/haberdasher/test.ts
  12. +3 −3 examples/authentication/src/server/services/index.ts
  13. +3 −3 examples/aws-lambda/src/habderdasher.ts
  14. +3 −5 examples/aws-lambda/src/haberdasher.pb.ts
  15. +2 −2 examples/aws-lambda/src/index.ts
  16. +3 −0 examples/config-exclude/.twirp.json
  17. +8 −0 examples/config-exclude/README.md
  18. +12 −0 examples/config-exclude/package.json
  19. +7 −0 examples/config-exclude/src/bar/B.proto
  20. +111 −0 examples/config-exclude/src/foo/A.pb.ts
  21. +5 −0 examples/config-exclude/src/foo/A.proto
  22. +13 −0 examples/config-exclude/tsconfig.json
  23. +4,815 −0 examples/config-exclude/yarn.lock
  24. +1 −1 examples/javascript-fullstack/src/client/index.jsx
  25. +1 −1 examples/javascript-fullstack/src/protos/haberdasher.pb.js
  26. +3 −3 examples/javascript-fullstack/src/server/haberdasher/index.js
  27. +2 −2 examples/javascript-fullstack/src/server/index.js
  28. +3 −5 examples/server-to-server/src/protos/haberdasher.pb.ts
  29. +3 −6 examples/server-to-server/src/server/haberdasher/index.ts
  30. +2 −2 examples/server-to-server/src/server/haberdasher/test.ts
  31. +2 −2 examples/server-to-server/src/server/index.ts
  32. +1 −1 examples/typescript-fullstack/src/client/index.tsx
  33. +3 −5 examples/typescript-fullstack/src/protos/haberdasher.pb.ts
  34. +2 −2 examples/typescript-fullstack/src/server/index.ts
  35. +3 −6 examples/typescript-fullstack/src/server/services/haberdasher/index.ts
  36. +2 −2 examples/typescript-fullstack/src/server/services/haberdasher/test.ts
  37. +1 −1 examples/typescript-fullstack/src/server/services/index.ts
  38. +1 −1 package.json
  39. +11 −9 src/autogenerate/index.ts
  40. +49 −3 src/cli/index.ts
  41. +92 −96 src/test-protos/__snapshots__/test.ts.snap
  42. +1 −1 src/test-serialization/message.pb.ts
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
# Changelog

## v0.0.46

This version has 3 breaking changes:

1. (Only impacts TypeScript users) The 'Service' naming suffix has been removed from the generated TypeScript types for services. Given the following proto:

```proto
service Haberdasher {
rpc MakeHat(Size) returns (Hat);
}
```

The generated service type will now be `Haberdasher` instead of `HaberdasherService`. This enables better out of the box compatibility with [buf](https://buf.build/) which expects all service names to end with `Service`. Following this recommendation would generate TwirpScript types with 'ServiceService' suffixes.

`<Service>Service => <Service>`

2. The 'Handler' suffix has been removed from the generated `create<Service>Handler` helper.

Given the proto above, the generated helper is now `createHaberdasher` instead of `createHaberdasherHandler`.

`create<Service>Handler=> create<Service>`

3. (Only impacts TypeScript users) `optional` types now accept `null` and `undefined`. This enables better compatibility with other tools that may type optionals as `some type | null`

Changes:

- remove naming suffixes by @tatethurston in https://github.com/tatethurston/TwirpScript/pull/125
- add exclude option to twirp.json by @tatethurston in https://github.com/tatethurston/TwirpScript/pull/127
- add null to optional ts types by @tatethurston in https://github.com/tatethurston/TwirpScript/pull/128

**Full Changelog**: https://github.com/tatethurston/TwirpScript/compare/v0.0.45...v0.0.46

## v0.0.45

- no longer generate `_readMessageJSON` for empty messages
128 changes: 99 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@ TwirpScript implements the latest [Twirp Wire Protocol (v7)](https://twitchtv.gi
- [Client](#client-3)
- [Server](#server-3)
- [Configuration 🛠](#configuration-)
- [JSON](#JSON)
- [Examples 🚀](#examples-)
- [Working with other tools](#working-with-other-tools)
- [Caveats, Warnings and Issues ⚠️](#caveats-warnings-and-issues-%EF%B8%8F)
@@ -182,12 +183,9 @@ If you have an existing Twirp server you're connecting to and only need a client
`src/server/haberdasher/index.ts`

```ts
import {
HaberdasherService,
createHaberdasherHandler,
} from "../../protos/haberdasher.pb";
import { Haberdasher, createHaberdasher } from "../../protos/haberdasher.pb";

const Haberdasher: HaberdasherService = {
const haberdasher: Haberdasher = {
MakeHat: (size) => {
return {
inches: size.inches,
@@ -197,7 +195,7 @@ const Haberdasher: HaberdasherService = {
},
};

export const HaberdasherHandler = createHaberdasherHandler(Haberdasher);
export const haberdasherHandler = createHaberdasher(haberdasher);
```

#### 5. Connect your service to your application server
@@ -207,11 +205,11 @@ export const HaberdasherHandler = createHaberdasherHandler(Haberdasher);
```ts
import { createServer } from "http";
import { createTwirpServer } from "twirpscript";
import { HaberdasherHandler } from "./haberdasher";
import { haberdasherHandler } from "./haberdasher";

const PORT = 8080;

const app = createTwirpServer([HaberdasherHandler]);
const app = createTwirpServer([haberdasherHandler]);

createServer(app).listen(PORT, () =>
console.log(`Server listening on port ${PORT}`)
@@ -309,12 +307,12 @@ Servers can be configured by passing a configuration object to `createTwirpServe
```ts
import { createServer } from "http";
import { createTwirpServer } from "twirpscript";
import { HaberdasherHandler } from "./haberdasher";
import { haberdasherHandler } from "./haberdasher";

const PORT = 8080;

// This removes the "/twirp" prefix in the RPC path
const app = createTwirpServer([HaberdasherHandler], { prefix: "" });
const app = createTwirpServer([haberdasherHandler], { prefix: "" });

createServer(app).listen(PORT, () =>
console.log(`Server listening on port ${PORT}`)
@@ -347,13 +345,10 @@ Custom fields can be added to the context object via [middleware](#middleware--i
If you setup middleware similiar to the [authentication middleware example](https://github.com/tatethurston/TwirpScript#example-3), you could read the `currentUser` `username` property in your service handler. See the [authentication example](https://github.com/tatethurston/twirpscript/tree/main/examples/authentication) for a full application.

```ts
import {
HaberdasherService,
createHaberdasherHandler,
} from "../../protos/haberdasher.pb";
import { Haberdasher, createHaberdasher } from "../../protos/haberdasher.pb";
import { Context } from "../some-path-to-your-definition";

const Haberdasher: HaberdasherService<Context> = {
const haberdasher: Haberdasher<Context> = {
MakeHat: (size, ctx) => {
return {
inches: size.inches,
@@ -363,7 +358,7 @@ const Haberdasher: HaberdasherService<Context> = {
},
};

export const HaberdasherHandler = createHaberdasherHandler(HaberdasherService);
export const haberdasherHandler = createHaberdasher(haberdasher);
```

### Middleware / Interceptors
@@ -425,18 +420,18 @@ The middleware handler will receive `req`, `context` and `next` parameters. `req
```ts
import { createServer } from "http";
import { createTwirpServer, TwirpError } from "twirpscript";
import { AuthenticationHandler } from "./authentication";
import { authenticationHandler } from "./authentication";

export interface Context {
currentUser: { username: string };
}

const services = [AuthenticationHandler]
const services = [authenticationHandler]
const app = createTwirpServer<Context, typeof services>(services);

app.use(async (req, ctx, next) => {
// exception so unauthenticated users can authenticate
if (ctx.service.name === AuthenticationHandler.name) {
if (ctx.service.name === authenticationHandler.name) {
return next();
}

@@ -537,11 +532,11 @@ response) have been written. Called with the current `context` and the response.
```ts
import { createServer } from "http";
import { createTwirpServer } from "twirpscript";
import { HaberdasherHandler } from "./haberdasher";
import { habderdasherHandler } from "./haberdasher";

const PORT = 8080;

const app = createTwirpServer([HaberdasherHandler]);
const app = createTwirpServer([habderdasherHandler]);

app.on("responseSent", (ctx) => {
// log or report
@@ -591,17 +586,53 @@ TwirpScript aims to be zero config, but can be configured by creating a `.twirp.
```
Setting `root` to `src`:
A.proto would `import` B.proto as follows:
```protobuf
import "B.proto";
```
TypeScript projects will generally want to set this value to match their `rootDir`, particularly when using [Protocol Buffers Well-Known Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) so that the generated well-known type files are under the `rootDir`.
// twirp.json
```json
{
"root": "src"
}
```
A.proto would `import` B.proto as follows:
```protobuf
import "B.proto";
```
TypeScript projects will generally want to set this value to match their `rootDir`, particularly when using [Protocol Buffers Well-Known Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) so that the generated well-known type files are under the `rootDir`.
</td>
<td>string (filepath)</td>
</tr>
<tr>
<td>exclude</td>
<td>
An array of patterns that should be skipped when searching for `.proto` files.
Example:
If we have the following project structure:
/src
/foo
A.proto
/bar
B.proto
Setting `exclude` to `["/bar/"]`:
// twirp.json
```json
{
exclude: ["/bar/"]
}
```
Will only process A.proto (B.proto) will be excluded from TwirpScript's code generation.
</td>
<td>string[] (RegExp pattern)</td>
</tr>
<tr>
<td>dest</td>
<td>
@@ -629,6 +660,13 @@ TwirpScript aims to be zero config, but can be configured by creating a `.twirp.
Setting `dest` to `out` will generate the following:
// twirp.json
```json
{
dest: 'out',
}
```
```
/src
A.proto
@@ -642,6 +680,14 @@ TwirpScript aims to be zero config, but can be configured by creating a `.twirp.
Note that the generated directory structure will mirror the `proto` paths exactly as is, only nested under the `dest` directory. If you want to change this, for instance, to omit `src` from the `out` directory above, you can set the `root`.
Setting `root` to `src` (in addition to setting `dest` to `out`) will generate the following:
// twirp.json
```json
{
root: 'src',
dest: 'out',
}
```
```
/src
@@ -666,6 +712,30 @@ TwirpScript aims to be zero config, but can be configured by creating a `.twirp.
</tbody>
</table>
## JSON
TwirpScript's JSON serialization/deserialization is migrating to the [proto3 specification](https://developers.google.com/protocol-buffers/docs/proto3#json). This is nearly complete, but still in progress.
TwirpScript will serialize JSON keys as `lowerCamelCase` versions of the proto field. Per the proto3 spec, the runtime will accept both `lowerCamelCase` and the original proto field name when deserializing. You can provide the `json_name` field option to specify an alternate key name. When doing so, the runtime will accept the `json_name` and the origin proto field name, but not `lowerCamelCase`.
### Example
```protobuf
syntax = "proto3";

message Hat {
// this key will serialize as 'userID' instead of 'userId'
int64 user_id = 1; [json_name="userID"];
int64 wardrobe_id = 2;
}
```
The above `Hat` message would serialize to the following JSON:
```json
{ "userID": "some 64bit number", "wardrobeId": "some 64bit number" }
```
## Examples 🚀
The documentation is a work in progress. Checkout the examples in the examples directory:
8 changes: 3 additions & 5 deletions clientcompat/src/clientcompat.pb.ts
Original file line number Diff line number Diff line change
@@ -70,17 +70,15 @@ export async function NoopMethodJSON(
}

//========================================//
// CompatService Service //
// CompatService //
//========================================//

export interface CompatServiceService<Context = unknown> {
export interface CompatService<Context = unknown> {
Method: (req: Req, context: Context) => Promise<Resp> | Resp;
NoopMethod: (empty: Empty, context: Context) => Promise<Empty> | Empty;
}

export function createCompatServiceHandler<Context>(
service: CompatServiceService<Context>
) {
export function createCompatService<Context>(service: CompatService<Context>) {
return {
name: "twirp.clientcompat.CompatService",
methods: {
2 changes: 1 addition & 1 deletion examples/authentication/src/client/index.tsx
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ const App: FC = () => {

return (
<div>
<h1>Haberdasher Service</h1>
<h1>Haberdasher </h1>
<h3>Current User: </h3>
{user ? (
<div>
8 changes: 4 additions & 4 deletions examples/authentication/src/protos/authentication.pb.ts
Original file line number Diff line number Diff line change
@@ -52,10 +52,10 @@ export async function LoginJSON(
}

//========================================//
// Authentication Service //
// Authentication //
//========================================//

export interface AuthenticationService<Context = unknown> {
export interface Authentication<Context = unknown> {
/**
* Login in a user
*/
@@ -65,8 +65,8 @@ export interface AuthenticationService<Context = unknown> {
) => Promise<CurrentUser> | CurrentUser;
}

export function createAuthenticationHandler<Context>(
service: AuthenticationService<Context>
export function createAuthentication<Context>(
service: Authentication<Context>
) {
return {
name: "Authentication",
8 changes: 3 additions & 5 deletions examples/authentication/src/protos/haberdasher.pb.ts
Original file line number Diff line number Diff line change
@@ -48,22 +48,20 @@ export async function MakeHatJSON(
}

//========================================//
// Haberdasher Service //
// Haberdasher //
//========================================//

/**
* Haberdasher service makes hats for clients.
*/
export interface HaberdasherService<Context = unknown> {
export interface Haberdasher<Context = unknown> {
/**
* MakeHat produces a hat of mysterious, randomly-selected color!
*/
MakeHat: (size: Size, context: Context) => Promise<Hat> | Hat;
}

export function createHaberdasherHandler<Context>(
service: HaberdasherService<Context>
) {
export function createHaberdasher<Context>(service: Haberdasher<Context>) {
return {
name: "Haberdasher",
methods: {
6 changes: 3 additions & 3 deletions examples/authentication/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { createServer } from "http";
import { createTwirpServer } from "twirpscript";
import { AuthenticationHandler, HaberdasherHandler } from "./services";
import { authenticationHandler, habderdasherHandler } from "./services";
import { Context } from "./context";
import { cors, requireAuthentication } from "./middleware";

const PORT = 8080;
const services = [AuthenticationHandler, HaberdasherHandler];
const services = [authenticationHandler, habderdasherHandler];

const app = createTwirpServer<Context, typeof services>(services)
.use(cors)
.use(requireAuthentication({ exceptions: [AuthenticationHandler.name] }));
.use(requireAuthentication({ exceptions: [authenticationHandler.name] }));

createServer(app).listen(PORT, () =>
console.log(`Server listening on port ${PORT}`)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IncomingMessage } from "http";
import { Middleware, TwirpErrorResponse } from "twirpscript";
import { Context } from "../context";
import { getCurrentUser, UnauthenticatedUser } from "../services";
import { getCurrentUser, unauthenticatedUser } from "../services";

interface RequireAuthenticationOpts {
exceptions: string[];
@@ -13,7 +13,7 @@ export function requireAuthentication({
return async (req, ctx, next) => {
for (let exception of exceptions) {
if (ctx.service?.name === exception) {
ctx.currentUser = UnauthenticatedUser;
ctx.currentUser = unauthenticatedUser;
return next();
}
}
Loading