-
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #226 from DestinyItemManager/sync-apps
Implement list/sync for apps
- Loading branch information
Showing
10 changed files
with
324 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { keyPath, ListToken } from '@stately-cloud/client'; | ||
import { ApiApp } from '../shapes/app.js'; | ||
import { client } from './client.js'; | ||
import { ApiApp as StatelyApiApp } from './generated/index.js'; | ||
|
||
/** | ||
* Get all registered apps. | ||
*/ | ||
export async function getAllApps(): Promise<[ApiApp[], ListToken]> { | ||
let apps = client.withAllowStale(true).beginList('/apps-1'); | ||
const allApps: ApiApp[] = []; | ||
let token: ListToken | undefined = undefined; | ||
|
||
while (true) { | ||
for await (const app of apps) { | ||
if (client.isType(app, 'ApiApp')) { | ||
allApps.push(convertToApiApp(app)); | ||
} | ||
} | ||
|
||
token = apps.token!; | ||
if (token.canContinue) { | ||
apps = client.continueList(token); | ||
} else { | ||
break; | ||
} | ||
} | ||
|
||
return [allApps, token] as const; | ||
} | ||
|
||
/** | ||
* Update the list of apps with changes from the server. | ||
*/ | ||
export async function updateApps(token: ListToken, apps: ApiApp[]): Promise<[ApiApp[], ListToken]> { | ||
const updates = client.syncList(token); | ||
for await (const update of updates) { | ||
switch (update.type) { | ||
case 'changed': { | ||
const item = update.item; | ||
if (client.isType(item, 'ApiApp')) { | ||
const existingIndex = apps.findIndex((app) => app.id === item.id); | ||
if (existingIndex >= 0) { | ||
apps[existingIndex] = convertToApiApp(item); | ||
} else { | ||
apps.push(convertToApiApp(item)); | ||
} | ||
} | ||
break; | ||
} | ||
case 'deleted': | ||
case 'updatedOutsideWindow': { | ||
const item = update.keyPath; | ||
// TODO: This could be easier! | ||
const id = /^\/apps\/app-([^/]+)$/.exec(item)?.[1]; | ||
const existingIndex = apps.findIndex((app) => app.id === id); | ||
if (existingIndex >= 0) { | ||
apps.splice(existingIndex, 1); | ||
} | ||
break; | ||
} | ||
case 'reset': { | ||
apps = []; | ||
break; | ||
} | ||
} | ||
} | ||
return [apps, updates.token!] as const; | ||
} | ||
|
||
/** | ||
* Get an app by its ID. | ||
*/ | ||
export async function getAppById(id: string): Promise<ApiApp | undefined> { | ||
const result = await client.get('ApiApp', keyPathFor(id)); | ||
if (result) { | ||
return convertToApiApp(result); | ||
} | ||
return undefined; | ||
} | ||
|
||
/** | ||
* Insert a new app into the list of registered apps, or update an existing app. | ||
*/ | ||
export async function insertApp(app: ApiApp): Promise<ApiApp> { | ||
let resultApp: ApiApp | undefined; | ||
// TODO: wish I could set an if-not-exists condition here, to avoid | ||
// accidentally updating an app. Instead I got a transaction. | ||
const result = await client.transaction(async (txn) => { | ||
const getResult = await txn.get('ApiApp', keyPathFor(app.id)); | ||
if (getResult) { | ||
resultApp = convertToApiApp(getResult); | ||
return; | ||
} | ||
await txn.put(client.create('ApiApp', { ...app, partition: 1n })); | ||
}); | ||
|
||
if (resultApp) { | ||
return resultApp; | ||
} | ||
|
||
if (result.committed && result.puts.length === 1) { | ||
const put = result.puts[0]; | ||
if (client.isType(put, 'ApiApp')) { | ||
return convertToApiApp(put); | ||
} | ||
} | ||
|
||
throw new Error('No ApiApp in result!'); | ||
} | ||
|
||
function keyPathFor(id: string) { | ||
return keyPath`/apps-1/app-${id}`; | ||
} | ||
|
||
// This mostly serves to remove the partition field, which we don't need. It | ||
// would cause problems serializing to JSON, since it's a bigint. It'd be nice | ||
// if I could've used a 32-bit int, but that isn't in the standard schema types... | ||
function convertToApiApp(app: StatelyApiApp): ApiApp { | ||
return { | ||
id: app.id, | ||
bungieApiKey: app.bungieApiKey, | ||
dimApiKey: app.dimApiKey, | ||
origin: app.origin, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Code generated by Stately. DO NOT EDIT. | ||
|
||
export * from './stately_item_types.js'; | ||
export * from './stately_pb.js'; | ||
export * from "./stately_pb.js"; | ||
export * from "./stately_item_types.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.