Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: 9.0.0 #431

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
9398d0b
feat(api): remove sunsetted spaces webhooks
meorphis Jun 18, 2024
94f6108
chore: rebuild project due to codegen change (#432)
stainless-app[bot] Jun 24, 2024
57614f0
feat(api): OpenAPI spec update via Stainless API (#433)
stainless-app[bot] Jun 24, 2024
6b022d0
feat(api): OpenAPI spec update via Stainless API (#434)
stainless-app[bot] Jun 24, 2024
3eb9196
chore(internal): minor reformatting (#435)
stainless-app[bot] Jun 25, 2024
b647830
feat(api): manual updates (#436)
stainless-app[bot] Jun 28, 2024
bb32c90
chore: gitignore test server logs (#437)
stainless-app[bot] Jun 28, 2024
9915dce
chore: rebuild project due to codegen change (#439)
stainless-app[bot] Jul 1, 2024
5c02800
chore(api): remove Spaces (#440)
stainless-app[bot] Jul 2, 2024
dcb9009
feat(api): generate webhook types (#441)
stainless-app[bot] Jul 3, 2024
7978973
feat(api): make webhooks a standalone api (#442)
Jul 4, 2024
06fe62a
chore: rebuild project due to codegen change (#444)
Jul 4, 2024
49159d3
chore: rebuild project due to codegen change (#447)
stainless-app[bot] Jul 9, 2024
4e3f947
feat(api): OpenAPI spec update via Stainless API (#448)
stainless-app[bot] Jul 11, 2024
d7c0cb1
chore: rebuild project due to codegen change (#449)
stainless-app[bot] Jul 11, 2024
d306284
chore: rebuild project due to codegen change (#450)
stainless-app[bot] Jul 11, 2024
5cf1cba
chore: rebuild project due to codegen change (#452)
Sep 5, 2024
e45308e
chore: rebuild project due to codegen change (#454)
stainless-app[bot] Jul 16, 2024
d42d2e2
chore: rebuild project due to codegen change (#455)
stainless-app[bot] Jul 18, 2024
be3320e
chore: rebuild project due to codegen change (#456)
stainless-app[bot] Jul 18, 2024
c1aee9d
chore(tests): update prism version (#457)
stainless-app[bot] Jul 23, 2024
96adde9
fix: use relative paths (#458)
Sep 5, 2024
576e0aa
feat(api): OpenAPI spec update via Stainless API (#459)
stainless-app[bot] Jul 25, 2024
66b4d45
fix(compat): remove ReadableStream polyfill redundant since node v16 …
stainless-app[bot] Jul 25, 2024
4370704
chore(docs): fix incorrect client var names (#461)
stainless-app[bot] Jul 26, 2024
2319c09
chore(internal): add constant for default timeout (#463)
stainless-app[bot] Jul 29, 2024
3e7edd5
chore(ci): correctly tag pre-release npm packages (#464)
stainless-app[bot] Jul 31, 2024
4a8888e
chore: rebuild project due to codegen change (#465)
stainless-app[bot] Aug 1, 2024
87858a2
chore: force eslint to use non flat config (#466)
stainless-app[bot] Aug 8, 2024
f3beb3a
chore(ci): minor changes (#467)
stainless-app[bot] Aug 9, 2024
89c0bbe
chore(ci): bump prism mock server version (#468)
stainless-app[bot] Aug 9, 2024
3649319
chore(examples): minor formatting changes (#469)
stainless-app[bot] Aug 12, 2024
a464948
chore: rebuild project due to codegen change (#470)
stainless-app[bot] Aug 19, 2024
6f2dae4
[video] video_quality as a replacement for encoding_tier (#303) (#472)
Sep 5, 2024
74e1ebf
chore(ci): check for build errors (#473)
stainless-app[bot] Aug 27, 2024
b887d28
chore: run tsc as part of lint script (#474)
stainless-app[bot] Aug 29, 2024
c20f418
chore: replace encoding tiers with video quality levels (#475)
stainless-app[bot] Aug 29, 2024
4e518a3
chore(ci): install deps via ./script/bootstrap (#476)
stainless-app[bot] Aug 29, 2024
37cc57c
fix(client): correct File construction from node-fetch Responses (#478)
stainless-app[bot] Sep 3, 2024
9a552f5
chore(internal): dependency updates (#479)
stainless-app[bot] Sep 4, 2024
34a8d90
chore(internal): minor bump qs version (#480)
stainless-app[bot] Sep 4, 2024
c7d79a0
chore: revert jose removal
yjp20 Sep 5, 2024
f24c5bd
fix(uploads): avoid making redundant memory copies (#483)
stainless-app[bot] Sep 5, 2024
5681f3a
fix file permissions
Sep 6, 2024
96bee84
chore(internal): codegen related update (#484)
stainless-app[bot] Sep 6, 2024
80a3f6c
chore(internal): codegen related update (#485)
stainless-app[bot] Sep 6, 2024
56485cc
fix(errors): pass message through to APIConnectionError (#486)
stainless-app[bot] Sep 9, 2024
56b3412
chore: better object fallback behaviour for casting errors (#487)
stainless-app[bot] Sep 9, 2024
f1bc68d
docs: update CONTRIBUTING.md (#488)
stainless-app[bot] Sep 13, 2024
2193260
chore(internal): add dev dependency (#489)
Sep 19, 2024
b1a56e2
chore(internal): fix some types (#491)
stainless-app[bot] Sep 17, 2024
6d7f768
fix(types): remove leftover polyfill usage (#492)
stainless-app[bot] Sep 18, 2024
f202eef
feat(client): send retry count header (#493)
stainless-app[bot] Sep 19, 2024
802475b
chore(internal): codegen related update (#494)
stainless-app[bot] Sep 25, 2024
1fe43bb
feat(api): api update (#495)
stainless-app[bot] Oct 2, 2024
d0b2805
feat(api): api update (#497)
stainless-app[bot] Oct 9, 2024
b873909
feat(api): api update (#500)
stainless-app[bot] Oct 18, 2024
4521afb
feat(api): api update (#501)
stainless-app[bot] Oct 22, 2024
2bb6de6
feat(api): api update (#502)
stainless-app[bot] Oct 23, 2024
8a158ea
chore: rebuild project due to codegen change (#507)
stainless-app[bot] Oct 28, 2024
6542115
chore: rebuild project due to codegen change (#508)
stainless-app[bot] Oct 28, 2024
7a5c790
feat(api): api update (#509)
stainless-app[bot] Oct 30, 2024
e334267
feat(api): api update (#510)
stainless-app[bot] Oct 31, 2024
281b74c
chore: rebuild project due to codegen change (#511)
Oct 31, 2024
72de44c
chore: rebuild project due to codegen change (#513)
stainless-app[bot] Nov 1, 2024
0d6f812
chore: rebuild project due to codegen change (#514)
stainless-app[bot] Nov 1, 2024
5e91cba
chore: rebuild project due to codegen change (#515)
stainless-app[bot] Nov 12, 2024
4d0e83c
Generate multiple tokens with one `signPlaybackId` call (#499)
decepulis Nov 14, 2024
87d6552
fix: ensure config for signPlaybackId can be optional (#516)
jsanford8 Nov 15, 2024
5016ad5
chore: rebuild project due to codegen change (#517)
stainless-app[bot] Nov 15, 2024
aab9b21
chore: rebuild project due to codegen change (#518)
stainless-app[bot] Nov 15, 2024
d0a7865
release: 9.0.0
stainless-app[bot] Nov 15, 2024
49342b3
update changelog
jsanford8 Nov 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Generate multiple tokens with one signPlaybackId call (#499)
* Generate multiple tokens with one `signPlaybackId` call

* Add missing DataTypeClaim

* wait, I thought of a better signature

* Revert "wait, I thought of a better signature"

This reverts commit 5fdefba.
  • Loading branch information
decepulis authored Nov 14, 2024
commit 4d0e83c8a4899ff1f5d9783163277ee423441252
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,57 @@ const statsToken = mux.jwt.signViewerCounts('some-live-stream-id', {
// https://stats.mux.com/counts?token={statsToken}
```

### Signing multiple JWTs at once
In cases you need multiple tokens, like when using Mux Player, things can get unwieldy pretty quickly. For example,
```tsx
const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "playback"
})
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "thumbnail",
})
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "storyboard"
})
const drmToken = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: "drm_license"
})

<mux-player
playback-token={playbackToken}
thumbanil-token={thumbnailToken}
storyboard-token={storyboardToken}
drm-token={drmToken}
playbackId={id}
></mux-player>
```

To simplify this use-case, you can provide multiple types to `signPlaybackId` to recieve multiple tokens. These tokens are provided in a format that Mux Player can take as props:
```tsx
// { "playback-token", "thumbnail-token", "storyboard-token", "drm-token" }
const tokens = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: ["playback", "thumbnail", "storyboard", "drm_license"]
})

<mux-player
{...tokens}
playbackId={id}
></mux-player>
```

If you would like to provide params to a single token (e.g., if you would like to have a thumbnail `time`), you can provide `[type, typeParams]` instead of `type`:
```tsx
const tokens = await mux.jwt.signPlaybackId(id, {
expiration: "1d",
type: ["playback", ["thumbnail", { time: 2 }], "storyboard", "drm_license"]
})
```

## Parsing Webhook payloads

To validate that the given payload was sent by Mux and parse the webhook payload for use in your application,
Expand Down
76 changes: 75 additions & 1 deletion deno_tests/api-resources/jwt.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// File generated from our OpenAPI spec by Stainless.

import Mux from '../../deno/mod.ts';
import { TypeClaim, DataTypeClaim } from '../../deno/util/jwt-types.ts';
import { TypeClaim, DataTypeClaim, TypeToken } from '../../deno/util/jwt-types.ts';
import { jwtVerify, importJWK, importPKCS8 } from 'https://deno.land/x/jose@v4.14.4/index.ts';
import { assertObjectMatch } from 'https://deno.land/std@0.197.0/testing/asserts.ts';
import { publicJwk, privatePkcs1, privatePkcs8 } from '../../tests/api-resources/rsaKeys.ts';
Expand Down Expand Up @@ -199,3 +199,77 @@ Deno.test(async function signViewerCounts() {
},
);
});

Deno.test('signPlaybackId multiple types returns same tokens as multiple single calls', async () => {
const id = 'abcdefgh';
const expiration = '1d';

const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'video',
});
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'thumbnail',
});
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'storyboard',
});
const drmToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'drm_license',
});
const gifToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'gif',
});
const statsToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'stats',
});

const tokens = await mux.jwt.signPlaybackId(id, {
expiration,
type: ['video', 'thumbnail', 'storyboard', 'drm_license', 'gif', 'stats'],
});

assertEquals(tokens[TypeToken.video], playbackToken);
assertEquals(tokens[TypeToken.thumbnail], thumbnailToken);
assertEquals(tokens[TypeToken.storyboard], storyboardToken);
assertEquals(tokens[TypeToken.drm_license], drmToken);
assertEquals(tokens[TypeToken.gif], gifToken);
assertEquals(tokens[TypeToken.stats], statsToken);
});

Deno.test(
'signPlaybackId multiple types with params returns same tokens as multiple single calls',
async () => {
const id = 'abcdefgh';
const expiration = '1d';

const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'video',
});
const thumbnailParams = { time: '2' };
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'thumbnail',
params: thumbnailParams,
});
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'storyboard',
});

const tokens = await mux.jwt.signPlaybackId(id, {
expiration,
type: ['video', ['thumbnail', thumbnailParams], 'storyboard'],
});

assertEquals(tokens[TypeToken.video], playbackToken);
assertEquals(tokens[TypeToken.thumbnail], thumbnailToken);
assertEquals(tokens[TypeToken.storyboard], storyboardToken);
},
);
70 changes: 67 additions & 3 deletions src/resources/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,45 @@ import { APIResource } from '@mux/mux-node/resource';
import * as jwt from '@mux/mux-node/_shims/auto/jwt';
import {
type SignOptions,
type MuxJWTSignOptions,
TypeClaim,
DataTypeClaim,
TypeToken,
type MuxJWTSignOptions,
type MuxJWTSignOptionsMultiple,
type Tokens,
isMuxJWTSignOptionsMultiple,
} from '@mux/mux-node/util/jwt-types';

export class Jwt extends APIResource {
async signPlaybackId(
playbackId: string,
config: MuxJWTSignOptions<keyof typeof TypeClaim>,
): Promise<string>;

async signPlaybackId(
playbackId: string,
config: MuxJWTSignOptionsMultiple<keyof typeof TypeClaim>,
): Promise<Tokens>;

/**
* Creates a new token to be used with a signed Playback ID
* Creates a new token or tokens to be used with a signed Playback ID
*/
async signPlaybackId(
playbackId: string,
config: MuxJWTSignOptions<keyof typeof TypeClaim> = {},
config:
| MuxJWTSignOptions<keyof typeof TypeClaim>
| MuxJWTSignOptionsMultiple<keyof typeof TypeClaim> = {},
): Promise<string | Tokens> {
if (isMuxJWTSignOptionsMultiple(config)) {
return this.signPlaybackIdMultipleTypes(playbackId, config);
} else {
return this.signPlaybackIdSingleType(playbackId, config);
}
}

private async signPlaybackIdSingleType(
playbackId: string,
config: MuxJWTSignOptions<keyof typeof TypeClaim>,
): Promise<string> {
const claim = TypeClaim[config.type ?? 'video'];
if (!claim) {
Expand All @@ -34,6 +61,43 @@ export class Jwt extends APIResource {
return jwt.sign(config.params ?? {}, await jwt.getPrivateKey(this._client, config), tokenOptions);
}

private async signPlaybackIdMultipleTypes(
playbackId: string,
config: MuxJWTSignOptionsMultiple<keyof typeof TypeClaim>,
): Promise<Tokens> {
const tokens: Tokens = {};

for (const typeOption of config.type) {
let type: keyof typeof TypeClaim;
let params: Record<string, string> | undefined;

if (Array.isArray(typeOption)) {
[type, params] = typeOption;
} else {
type = typeOption;
params = undefined;
}

const singleConfig = {
...config,
type,
params: {
...config.params,
...params,
},
};

const token = await this.signPlaybackIdSingleType(playbackId, singleConfig);

const tokenKey = TypeToken[type];
if (tokenKey) {
tokens[tokenKey] = token;
}
}

return tokens;
}

/**
* Creates a new token for a license for playing back DRM'd video content
*/
Expand Down
39 changes: 30 additions & 9 deletions src/util/jwt-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ export interface JwtHeader {
x5c?: string | string[] | undefined;
}

export interface MuxJWTSignOptions<Type extends string = string> {
keyId?: string;
keySecret?: string | PrivateKey;
keyFilePath?: string;
type?: Type;
expiration?: string;
params?: Record<string, string>;
}

export enum TypeClaim {
video = 'v',
thumbnail = 't',
Expand All @@ -51,6 +42,36 @@ export enum TypeClaim {
stats = 'playback_id',
drm_license = 'd',
}
export enum TypeToken {
video = 'playback-token',
thumbnail = 'thumbnail-token',
storyboard = 'storyboard-token',
drm_license = 'drm-token',
gif = 'gif-token', // Not supported by Mux Player
stats = 'stats-token', // Not supported by Mux Player
}
export type TypeTokenValues = (typeof TypeToken)[keyof typeof TypeToken];
export type Tokens = Partial<Record<TypeTokenValues, string>>;
// ['thumbnail', { time: 2 }]
export type TypeWithParams<Type extends string = string> = [Type, MuxJWTSignOptions<Type>['params']];

interface MuxJWTSignOptionsBase<Type extends string = string> {
keyId?: string;
keySecret?: string | PrivateKey;
keyFilePath?: string;
type?: Type | Array<Type | TypeWithParams<Type>>;
expiration?: string;
params?: Record<string, string>;
}
export interface MuxJWTSignOptions<Type extends string = string> extends MuxJWTSignOptionsBase<Type> {
type?: Type;
}
export interface MuxJWTSignOptionsMultiple<Type extends string = string> extends MuxJWTSignOptionsBase<Type> {
type: Array<Type | TypeWithParams<Type>>;
}
export const isMuxJWTSignOptionsMultiple = (
config: MuxJWTSignOptions | MuxJWTSignOptionsMultiple,
): config is MuxJWTSignOptionsMultiple => Array.isArray(config.type);

export enum DataTypeClaim {
video = 'video_id',
Expand Down
73 changes: 72 additions & 1 deletion tests/api-resources/jwt.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// File generated from our OpenAPI spec by Stainless.

import Mux from '@mux/mux-node';
import { TypeClaim, DataTypeClaim } from '@mux/mux-node/util/jwt-types';
import { TypeClaim, DataTypeClaim, TypeToken } from '@mux/mux-node/util/jwt-types';
import { decodeJwt, jwtVerify, importJWK, importPKCS8 } from 'jose';
import { publicJwk, privatePkcs1, privatePkcs8 } from './rsaKeys';
import crypto from 'crypto';
Expand Down Expand Up @@ -277,4 +277,75 @@ describe('resource jwt', () => {
sub: 'abcdefgh',
});
});

test('signPlaybackId multiple types returns same tokens as multiple single calls', async () => {
const id = 'abcdefgh';
const expiration = '1d';

const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'video',
});
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'thumbnail',
});
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'storyboard',
});
const drmToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'drm_license',
});
const gifToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'gif',
});
const statsToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'stats',
});

const tokens = await mux.jwt.signPlaybackId(id, {
expiration,
type: ['video', 'thumbnail', 'storyboard', 'drm_license', 'gif', 'stats'],
});

expect(tokens[TypeToken.video]).toEqual(playbackToken);
expect(tokens[TypeToken.thumbnail]).toEqual(thumbnailToken);
expect(tokens[TypeToken.storyboard]).toEqual(storyboardToken);
expect(tokens[TypeToken.drm_license]).toEqual(drmToken);
expect(tokens[TypeToken.gif]).toEqual(gifToken);
expect(tokens[TypeToken.stats]).toEqual(statsToken);
});

test('signPlaybackId multiple types with params returns same tokens as multiple single calls', async () => {
const id = 'abcdefgh';
const expiration = '1d';

const playbackToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'video',
});
const thumbnailParams = { time: '2' };
const thumbnailToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'thumbnail',
params: thumbnailParams,
});
const storyboardToken = await mux.jwt.signPlaybackId(id, {
expiration,
type: 'storyboard',
});

const tokens = await mux.jwt.signPlaybackId(id, {
expiration,
type: ['video', ['thumbnail', thumbnailParams], 'storyboard'],
});

expect(tokens[TypeToken.video]).toEqual(playbackToken);
expect(tokens[TypeToken.thumbnail]).toEqual(thumbnailToken);
expect(tokens[TypeToken.storyboard]).toEqual(storyboardToken);
});
});