Skip to content

Commit

Permalink
Use auth cache as default source of truth across providers
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepyfran committed Dec 25, 2024
1 parent 000f3a1 commit 0fa5b96
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export const OneDriveProviderLive = Layer.effect(

return MediaProviderFactory.of({
authenticationProvider: Effect.succeed(msalAuth),
createMediaProvider: (authInfo) => {
createMediaProvider: (fallbackAuthInfo) => {
const options: ClientOptions = {
authProvider: {
getAccessToken: () =>
Effect.runPromise(
authCache.get(FileBasedProviderId.OneDrive).pipe(
Effect.map(Option.getOrElse(() => authInfo)),
Effect.map(Option.getOrElse(() => fallbackAuthInfo)),
Effect.map((authInfo) => authInfo.accessToken),
),
),
Expand Down
11 changes: 10 additions & 1 deletion packages/infrastructure/spotify-player/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
ApiBasedProviderId,
AuthenticationCache,
MediaPlayerFactory,
MediaPlayerId,
ProviderType,
Expand All @@ -12,6 +14,7 @@ import {
Effect,
Layer,
Match,
Option,
pipe,
Queue,
Scope,
Expand Down Expand Up @@ -42,6 +45,7 @@ const { PlayTrack, TogglePlayback, Stop, Dispose } =
Data.taggedEnum<SpotifyPlayerCommand>();

const make = Effect.gen(function* () {
const authCache = yield* AuthenticationCache;
const playerApi = yield* SpotifyPlayerApi;
const layerScope = yield* Scope.make();

Expand Down Expand Up @@ -73,7 +77,12 @@ const make = Effect.gen(function* () {
window.onSpotifyWebPlaybackSDKReady = () => {
const player = new window.Spotify.Player({
name: "Echo",
getOAuthToken: (cb) => cb(authInfo.accessToken),
getOAuthToken: (cb) =>
Effect.runPromise(
authCache
.get(ApiBasedProviderId.Spotify)
.pipe(Effect.map(Option.getOrElse(() => authInfo))),
).then((authInfo) => cb(authInfo.accessToken)),
volume: 1.0,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
type Album,
type Artist,
type AuthenticationInfo,
type IAuthenticationCache,
type Track,
} from "@echo/core-types";
import type { SpotifyAlbumResponse } from "./types";
Expand All @@ -22,7 +23,8 @@ const initialState = {
* Creates an effect that retrieves all the albums in the user's library.
*/
export const createListAlbums = (
authInfo: AuthenticationInfo,
fallbackAuthInfo: AuthenticationInfo,
authCache: IAuthenticationCache,
userLibraryApi: ISpotifyLibraryApi,
) =>
Effect.iterate(initialState, {
Expand All @@ -35,9 +37,13 @@ export const createListAlbums = (
return state;
}

const authInfoOrFallback = yield* authCache
.get(ApiBasedProviderId.Spotify)
.pipe(Effect.map(Option.getOrElse(() => fallbackAuthInfo)));

const response = yield* userLibraryApi
.savedAlbums({
authInfo,
authInfo: authInfoOrFallback,
offset: maybeOffset.value,
limit: 50,
})
Expand Down
15 changes: 12 additions & 3 deletions packages/infrastructure/spotify-provider/src/spotify-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { MediaProviderFactory, ProviderType } from "@echo/core-types";
import {
AuthenticationCache,
MediaProviderFactory,
ProviderType,
} from "@echo/core-types";
import { Effect, Layer } from "effect";
import { SpotifyAuthentication } from "./spotify-authentication";
import { SpotifyLibraryApi } from "./apis/user-library-api";
Expand All @@ -13,14 +17,19 @@ import { createListAlbums } from "./apis/list-albums-api";
export const SpotifyProviderLive = Layer.effect(
MediaProviderFactory,
Effect.gen(function* () {
const authCache = yield* AuthenticationCache;
const spotifyAuth = yield* SpotifyAuthentication;
const userLibraryApi = yield* SpotifyLibraryApi;

return MediaProviderFactory.of({
authenticationProvider: Effect.succeed(spotifyAuth),
createMediaProvider: (authInfo) => ({
createMediaProvider: (fallbackAuthInfo) => ({
_tag: ProviderType.ApiBased,
listAlbums: createListAlbums(authInfo, userLibraryApi),
listAlbums: createListAlbums(
fallbackAuthInfo,
authCache,
userLibraryApi,
),
}),
});
}),
Expand Down
34 changes: 23 additions & 11 deletions packages/services/bootstrap/src/loaders/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type ProviderMetadata,
MediaPlayerFactory,
ProviderType,
AuthenticationCache,
} from "@echo/core-types";
import { FetchHttpClient } from "@effect/platform";

Expand All @@ -27,7 +28,9 @@ export class LazyLoadedMediaPlayer extends Effect.Tag(
*/
const lazyLoadFromMetadata = (
metadata: ProviderMetadata,
): Effect.Effect<Layer.Layer<MediaPlayerFactory, never, never>> => {
): Effect.Effect<
Layer.Layer<MediaPlayerFactory, never, AuthenticationCache>
> => {
switch (metadata.type) {
case ProviderType.FileBased:
return Effect.promise(async () => {
Expand Down Expand Up @@ -61,17 +64,26 @@ const createLazyLoadedMediaPlayer = (metadata: ProviderMetadata) =>
/**
* A layer that can lazily load a media player based on the metadata provided.
*/
export const LazyLoadedMediaPlayerLive = Layer.succeed(
export const LazyLoadedMediaPlayerLive = Layer.effect(
LazyLoadedMediaPlayer,
LazyLoadedMediaPlayer.of({
load: (metadata) =>
Effect.gen(function* () {
const mediaPlayerFactory = yield* lazyLoadFromMetadata(metadata);
Effect.gen(function* () {
const authCache = yield* AuthenticationCache;
const authCacheLayer = Layer.succeed(AuthenticationCache, authCache);

return yield* Effect.provide(
createLazyLoadedMediaPlayer(metadata),
mediaPlayerFactory,
);
}),
return LazyLoadedMediaPlayer.of({
load: (metadata) =>
Effect.gen(function* () {
const mediaPlayerFactory = yield* lazyLoadFromMetadata(metadata);

return yield* Effect.provide(
createLazyLoadedMediaPlayer(metadata),
// The auth cache requires a single instance for the whole application
// since the tokens are only stored in-memory, but since providers
// are lazy loaded and require the dependencies in place, we need
// to manually provide it from the environment.
mediaPlayerFactory.pipe(Layer.provide(authCacheLayer)),
);
}),
});
}),
);

0 comments on commit 0fa5b96

Please sign in to comment.