Skip to content

Commit 0fa5b96

Browse files
committed
Use auth cache as default source of truth across providers
1 parent 000f3a1 commit 0fa5b96

File tree

5 files changed

+55
-19
lines changed

5 files changed

+55
-19
lines changed

packages/infrastructure/onedrive-provider/src/onedrive-provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ export const OneDriveProviderLive = Layer.effect(
2626

2727
return MediaProviderFactory.of({
2828
authenticationProvider: Effect.succeed(msalAuth),
29-
createMediaProvider: (authInfo) => {
29+
createMediaProvider: (fallbackAuthInfo) => {
3030
const options: ClientOptions = {
3131
authProvider: {
3232
getAccessToken: () =>
3333
Effect.runPromise(
3434
authCache.get(FileBasedProviderId.OneDrive).pipe(
35-
Effect.map(Option.getOrElse(() => authInfo)),
35+
Effect.map(Option.getOrElse(() => fallbackAuthInfo)),
3636
Effect.map((authInfo) => authInfo.accessToken),
3737
),
3838
),

packages/infrastructure/spotify-player/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
ApiBasedProviderId,
3+
AuthenticationCache,
24
MediaPlayerFactory,
35
MediaPlayerId,
46
ProviderType,
@@ -12,6 +14,7 @@ import {
1214
Effect,
1315
Layer,
1416
Match,
17+
Option,
1518
pipe,
1619
Queue,
1720
Scope,
@@ -42,6 +45,7 @@ const { PlayTrack, TogglePlayback, Stop, Dispose } =
4245
Data.taggedEnum<SpotifyPlayerCommand>();
4346

4447
const make = Effect.gen(function* () {
48+
const authCache = yield* AuthenticationCache;
4549
const playerApi = yield* SpotifyPlayerApi;
4650
const layerScope = yield* Scope.make();
4751

@@ -73,7 +77,12 @@ const make = Effect.gen(function* () {
7377
window.onSpotifyWebPlaybackSDKReady = () => {
7478
const player = new window.Spotify.Player({
7579
name: "Echo",
76-
getOAuthToken: (cb) => cb(authInfo.accessToken),
80+
getOAuthToken: (cb) =>
81+
Effect.runPromise(
82+
authCache
83+
.get(ApiBasedProviderId.Spotify)
84+
.pipe(Effect.map(Option.getOrElse(() => authInfo))),
85+
).then((authInfo) => cb(authInfo.accessToken)),
7786
volume: 1.0,
7887
});
7988

packages/infrastructure/spotify-provider/src/apis/list-albums-api.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
type Album,
1010
type Artist,
1111
type AuthenticationInfo,
12+
type IAuthenticationCache,
1213
type Track,
1314
} from "@echo/core-types";
1415
import type { SpotifyAlbumResponse } from "./types";
@@ -22,7 +23,8 @@ const initialState = {
2223
* Creates an effect that retrieves all the albums in the user's library.
2324
*/
2425
export const createListAlbums = (
25-
authInfo: AuthenticationInfo,
26+
fallbackAuthInfo: AuthenticationInfo,
27+
authCache: IAuthenticationCache,
2628
userLibraryApi: ISpotifyLibraryApi,
2729
) =>
2830
Effect.iterate(initialState, {
@@ -35,9 +37,13 @@ export const createListAlbums = (
3537
return state;
3638
}
3739

40+
const authInfoOrFallback = yield* authCache
41+
.get(ApiBasedProviderId.Spotify)
42+
.pipe(Effect.map(Option.getOrElse(() => fallbackAuthInfo)));
43+
3844
const response = yield* userLibraryApi
3945
.savedAlbums({
40-
authInfo,
46+
authInfo: authInfoOrFallback,
4147
offset: maybeOffset.value,
4248
limit: 50,
4349
})

packages/infrastructure/spotify-provider/src/spotify-provider.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { MediaProviderFactory, ProviderType } from "@echo/core-types";
1+
import {
2+
AuthenticationCache,
3+
MediaProviderFactory,
4+
ProviderType,
5+
} from "@echo/core-types";
26
import { Effect, Layer } from "effect";
37
import { SpotifyAuthentication } from "./spotify-authentication";
48
import { SpotifyLibraryApi } from "./apis/user-library-api";
@@ -13,14 +17,19 @@ import { createListAlbums } from "./apis/list-albums-api";
1317
export const SpotifyProviderLive = Layer.effect(
1418
MediaProviderFactory,
1519
Effect.gen(function* () {
20+
const authCache = yield* AuthenticationCache;
1621
const spotifyAuth = yield* SpotifyAuthentication;
1722
const userLibraryApi = yield* SpotifyLibraryApi;
1823

1924
return MediaProviderFactory.of({
2025
authenticationProvider: Effect.succeed(spotifyAuth),
21-
createMediaProvider: (authInfo) => ({
26+
createMediaProvider: (fallbackAuthInfo) => ({
2227
_tag: ProviderType.ApiBased,
23-
listAlbums: createListAlbums(authInfo, userLibraryApi),
28+
listAlbums: createListAlbums(
29+
fallbackAuthInfo,
30+
authCache,
31+
userLibraryApi,
32+
),
2433
}),
2534
});
2635
}),

packages/services/bootstrap/src/loaders/player.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
type ProviderMetadata,
44
MediaPlayerFactory,
55
ProviderType,
6+
AuthenticationCache,
67
} from "@echo/core-types";
78
import { FetchHttpClient } from "@effect/platform";
89

@@ -27,7 +28,9 @@ export class LazyLoadedMediaPlayer extends Effect.Tag(
2728
*/
2829
const lazyLoadFromMetadata = (
2930
metadata: ProviderMetadata,
30-
): Effect.Effect<Layer.Layer<MediaPlayerFactory, never, never>> => {
31+
): Effect.Effect<
32+
Layer.Layer<MediaPlayerFactory, never, AuthenticationCache>
33+
> => {
3134
switch (metadata.type) {
3235
case ProviderType.FileBased:
3336
return Effect.promise(async () => {
@@ -61,17 +64,26 @@ const createLazyLoadedMediaPlayer = (metadata: ProviderMetadata) =>
6164
/**
6265
* A layer that can lazily load a media player based on the metadata provided.
6366
*/
64-
export const LazyLoadedMediaPlayerLive = Layer.succeed(
67+
export const LazyLoadedMediaPlayerLive = Layer.effect(
6568
LazyLoadedMediaPlayer,
66-
LazyLoadedMediaPlayer.of({
67-
load: (metadata) =>
68-
Effect.gen(function* () {
69-
const mediaPlayerFactory = yield* lazyLoadFromMetadata(metadata);
69+
Effect.gen(function* () {
70+
const authCache = yield* AuthenticationCache;
71+
const authCacheLayer = Layer.succeed(AuthenticationCache, authCache);
7072

71-
return yield* Effect.provide(
72-
createLazyLoadedMediaPlayer(metadata),
73-
mediaPlayerFactory,
74-
);
75-
}),
73+
return LazyLoadedMediaPlayer.of({
74+
load: (metadata) =>
75+
Effect.gen(function* () {
76+
const mediaPlayerFactory = yield* lazyLoadFromMetadata(metadata);
77+
78+
return yield* Effect.provide(
79+
createLazyLoadedMediaPlayer(metadata),
80+
// The auth cache requires a single instance for the whole application
81+
// since the tokens are only stored in-memory, but since providers
82+
// are lazy loaded and require the dependencies in place, we need
83+
// to manually provide it from the environment.
84+
mediaPlayerFactory.pipe(Layer.provide(authCacheLayer)),
85+
);
86+
}),
87+
});
7688
}),
7789
);

0 commit comments

Comments
 (0)