Skip to content

Commit

Permalink
Merge pull request #40 from rstudio/aron-v1-content-environment
Browse files Browse the repository at this point in the history
connect: use the public /v1/content/{guid}/environment endpoints
  • Loading branch information
tdstein authored Oct 7, 2024
2 parents f272cff + 3be930d commit 2b47d66
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 67 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# rsconnect-ts

TypeScript client library for the Posit Connect API
A TypeScript client library for the Posit Connect API

> :warning: This is a *beta-quality* library. Please see
> [rstudio/actions/connect-publish](https://github.com/rstudio/actions/tree/main/connect-publish)
Expand Down
49 changes: 26 additions & 23 deletions __tests__/EnvironmentUpdater.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@ import * as rsconnect from '../src/main'

describe('EnvironmentUpdater', () => {
let eu: rsconnect.EnvironmentUpdater
let client: FakeAPIClient

beforeEach(() => {
eu = new rsconnect.EnvironmentUpdater(new FakeAPIClient())
client = new FakeAPIClient()
eu = new rsconnect.EnvironmentUpdater(client)
process.chdir = jest.fn()
})

describe('updateAppEnvironment', () => {
it('works', async () => {
process.env.CONNECT_ENV_SET_SECRET = 'eye of newt'
const env = await eu.updateAppEnvironment(42, './somewhar/up/er')
expect(env.get('MODE')).toBe('ludicrous')
expect(env.get('SECRET')).toBe('eye of newt')
const vars = await eu.updateAppEnvironment('the-content-guid', './somewhar/up/er')
expect(vars.sort((a, b) => a.localeCompare(b))).toStrictEqual(['MODE', 'SECRET'])
expect(client.fakeState).toStrictEqual({
'the-content-guid': {
MODE: 'ludicrous',
SECRET: 'eye of newt'
}
})
})
})
})
Expand All @@ -27,31 +34,27 @@ class FakeAPIClient extends rsconnect.APIClient {
apiKey: 'notAsEcRet'
})
this.fakeState = {
42: {
appId: 42,
version: 4,
values: {
MODE: 'ludicrous'
}
'the-content-guid': {
MODE: 'ludicrous'
}
}
}

public async getAppEnvironment (appID: number): Promise<any> {
return this.fakeState[appID]
public async getAppEnvironment (appGUID: string): Promise<any> {
const state = this.fakeState[appGUID] !== undefined ? this.fakeState[appGUID] : {}
return Object.keys(state)
}

public async updateAppEnvironment (appID: number, version: number, env: rsconnect.Environment): Promise<any> {
const origState = this.fakeState[appID] !== undefined ? this.fakeState[appID] : { values: {} }
this.fakeState[appID] = {
...origState,
appId: appID,
version,
values: {
...origState.values,
...Object.fromEntries(env.entries())
public async updateAppEnvironment (appGUID: string, env: rsconnect.Environment): Promise<any> {
const state = this.fakeState[appGUID] !== undefined ? this.fakeState[appGUID] : {}
Array.from(env.keys()).forEach(key => {
const value = env.get(key)
if (value === null || value === undefined) {
state.delete(key)
} else {
state[key] = value
}
}
return {}
})
this.fakeState[appGUID] = state
}
}
21 changes: 9 additions & 12 deletions src/APIClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import qs from 'qs'
import { debugLog, debugEnabled } from './debugLog'
import {
Application,
AppEnvironmentResponse,
ClientTaskResponse,
ExtendedBundleResponse,
ListApplicationsParams,
Expand Down Expand Up @@ -139,19 +138,17 @@ export class APIClient {
})
}

public async getAppEnvironment (appID: number): Promise<AppEnvironmentResponse> {
return await this.client.get(`applications/${appID}/environment`)
.then((resp: AxiosResponse) => {
const camelized = keysToCamel(resp.data)
camelized.values = resp.data.values
return camelized
})
public async getAppEnvironment (appGUID: string): Promise<string[]> {
return await this.client.get(`v1/content/${appGUID}/environment`)
.then((resp: AxiosResponse) => resp.data)
}

public async updateAppEnvironment (appID: number, version: number, env: Environment): Promise<AxiosResponse> {
return await this.client.post(
`applications/${appID}/environment`,
{ app_id: appID, version, values: Object.fromEntries(env.entries()) }
public async updateAppEnvironment (appGUID: string, env: Environment): Promise<AxiosResponse> {
return await this.client.patch(
`v1/content/${appGUID}/environment`,
Array.from(env.keys()).map(key => {
return { name: key, value: env.get(key) }
})
)
}

Expand Down
41 changes: 17 additions & 24 deletions src/EnvironmentUpdater.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { APIClient } from './APIClient'
import { Environment } from './Environment'
import { AppEnvironmentResponse } from './api-types'
import { debugLog } from './debugLog'

export class EnvironmentUpdater {
Expand All @@ -10,14 +9,13 @@ export class EnvironmentUpdater {
this.client = client
}

public async updateAppEnvironment (appID: number, dir: string = '.'): Promise<Environment> {
public async updateAppEnvironment (appGUID: string, dir: string = '.'): Promise<string[]> {
debugLog(() => [
'EnvironmentUpdater: updating environment for',
`app=${appID}`,
'EnvironmentUpdater: loading environment for',
`app=${appGUID}`,
`dir=${dir}`
].join(' '))

const { version } = await this.client.getAppEnvironment(appID)
const curDir = process.cwd()
const env = new Environment()

Expand All @@ -31,33 +29,28 @@ export class EnvironmentUpdater {
if (env.size === 0) {
debugLog(() => [
'EnvironmentUpdater: no environment variables found for',
`app=${appID}`,
`app=${appGUID}`,
`dir=${dir}`
].join(' '))
} else {
debugLog(() => [
'EnvironmentUpdater: updating environment for',
`app=${appGUID}`,
`dir=${dir}`
].join(' '))
return env
}

debugLog(() => [
'EnvironmentUpdater: updating environment based on',
`version=${version}`,
'for',
`app=${appID}`,
`dir=${dir}`
].join(' '))
await this.client.updateAppEnvironment(appGUID, env)
}

await this.client.updateAppEnvironment(appID, version, env)
return await this.client.getAppEnvironment(appID)
.then((resp: AppEnvironmentResponse) => {
return await this.client.getAppEnvironment(appGUID)
.then((resp: string[]) => {
debugLog(() => [
'EnvironmentUpdater: converting env object to map',
'EnvironmentUpdater: active environment variables for',
`app=${appGUID}`,
`resp=${JSON.stringify(resp)}`
].join(' '))

const newEnv = new Environment()
for (const key in resp.values) {
newEnv.set(key, resp.values[key])
}
return newEnv
return resp
})
}
}
7 changes: 0 additions & 7 deletions src/api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ export interface AppGitRecord {
lastError: string
}

export interface AppEnvironmentResponse {
appId: number
appGuid: string
version: number
values: any
}

export interface Application {
id: number
guid: string
Expand Down

0 comments on commit 2b47d66

Please sign in to comment.