Skip to content

Commit

Permalink
Merge pull request #387 from rstudio/dotnomad/deployment-destinations
Browse files Browse the repository at this point in the history
List Destinations on the Project page
  • Loading branch information
dotNomad authored Nov 29, 2023
2 parents 5fe2170 + 0e4d5f0 commit 2e06533
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 0 deletions.
9 changes: 9 additions & 0 deletions web/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import camelcaseKeys from 'camelcase-keys';
import decamelizeKeys from 'decamelize-keys';

import { Deployments } from 'src/api/resources/Deployments';
import { Configurations } from 'src/api/resources/Configurations';

const camelCaseInterceptor = (response: AxiosResponse): AxiosResponse => {
if (response.data && response.headers['content-type'] === 'application/json') {
response.data = camelcaseKeys(
Expand All @@ -22,6 +25,9 @@ const snakeCaseInterceptor = (config: InternalAxiosRequestConfig) => {
};

class PublishingClientApi {
configurations: Configurations;
deployments: Deployments;

constructor() {
const client = axios.create({
baseURL: './api',
Expand All @@ -30,6 +36,9 @@ class PublishingClientApi {

client.interceptors.request.use(snakeCaseInterceptor);
client.interceptors.response.use(camelCaseInterceptor);

this.configurations = new Configurations(client);
this.deployments = new Deployments(client);
}
}

Expand Down
19 changes: 19 additions & 0 deletions web/src/api/resources/Configurations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (C) 2023 by Posit Software, PBC.

import { AxiosInstance } from 'axios';

import { Configuration, ConfigurationError } from 'src/api/types/configurations';

export class Configurations {
private client: AxiosInstance;

constructor(client: AxiosInstance) {
this.client = client;
}

getAll() {
return this.client.get<Array<Configuration | ConfigurationError>>(
'/configurations',
);
}
}
34 changes: 34 additions & 0 deletions web/src/api/resources/Deployments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2023 by Posit Software, PBC.

import { AxiosInstance, AxiosRequestConfig } from 'axios';

import { Deployment, DeploymentError } from 'src/api/types/deployments';

export class Deployments {
private client: AxiosInstance;

constructor(client: AxiosInstance) {
this.client = client;
}

private createConfig(config?: AxiosRequestConfig): AxiosRequestConfig {
return {
ignoreCamelCase: ['files'],
...config,
};
}

getAll() {
return this.client.get<Array<Deployment | DeploymentError>>(
'/deployments',
this.createConfig()
);
}

get(id: string) {
return this.client.get<Deployment | DeploymentError>(
`deployments/${id}`,
this.createConfig()
);
}
}
110 changes: 110 additions & 0 deletions web/src/api/types/configurations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (C) 2023 by Posit Software, PBC.

import { ConnectConfig } from 'src/api/types/connect';
import { SchemaURL } from 'src/api/types/schema';

export type ConfigurationLocation = {
configurationName: string;
configurationPath: string;
}

export type ConfigurationError = {
error: string;
} & ConfigurationLocation

export type Configuration = {
configuration: ConfigurationDetails
} & ConfigurationLocation

export function isConfigurationError(
c: Configuration | ConfigurationError
): c is ConfigurationError {
return (c as ConfigurationError).error !== undefined;
}

enum AppMode {
UNKNOWN = '',
SHINY = 'shiny',
RMD_SHINY = 'rmd-shiny',
RMD_STATIC = 'rmd-static',
STATIC = 'static',
PLUMBER_API = 'api',
JUPYTER_STATIC = 'jupyter-static',
JUPYTER_VOILA = 'jupyter-voila',
PYTHON_API = 'python-api',
PYTHON_DASH = 'python-dash',
PYTHON_STREAMLIT = 'python-streamlit',
PYTHON_BOKEH = 'python-bokeh',
PYTHON_FASTAPI = 'python-fastapi',
PYTHON_SHINY = 'python-shiny',
QUARTO_SHINY = 'quarto-shiny',
QUARTO_STATIC = 'quarto-static',
}

export type ConfigurationDetails = {
$schema: SchemaURL,
type: AppMode,
entrypoint?: string,
title?: string,
description?: string,
thumbnail?: string,
tags?: string[],
python?: PythonConfig,
r?: RConfig,
quarto?: QuartoConfig,
environment?: EnvironmentConfig,
secrets?: string[],
schedules?: ScheduleConfig[],
access?: AccessConfig,
connect?: ConnectConfig,
}

export type PythonConfig = {
version: string,
packageFile: string,
packageManager: string
}

export type RConfig = {
version: string,
packageFile: string,
packageManager: string
}

export type QuartoConfig = {
version: string,
engines: string[]
}

export type EnvironmentConfig = Record<string, string>

export type ScheduleConfig = {
start: string,
recurrence: string
}

export enum AccessType {
ANONYMOUS = 'all',
LOGGED_IN = 'logged-in',
ACL = 'acl'
}

export type AccessConfig = {
type: AccessType,
users?: User[],
groups?: Group[]
}

export type User = {
id?: string,
guid?: string,
name?: string,
permissions: string
}

export type Group = {
id?: string,
guid?: string,
name?: string,
permissions: string
}
34 changes: 34 additions & 0 deletions web/src/api/types/connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (C) 2023 by Posit Software, PBC.

export type ConnectConfig = {
access?: ConnectAccess,
runtime?: ConnectRuntime,
kubernetes?: ConnectKubernetes
}

export type ConnectAccess = {
runAs?: string,
runAsCurrentUser?: boolean,
}

export type ConnectRuntime = {
connectTimeout?: number,
readTimeout?: number,
initTimeout?: number,
idleTimeout?: number,
maxProcesses?: number,
minProcesses?: number,
maxConnections?: number,
loadFactor?: number,
}

export type ConnectKubernetes = {
memoryRequest?: number,
memoryLimit?: number,
cpuRequest?: number,
cpuLimit?: number,
amdGpuLimit?: number,
nvidiaGpuLimit?: number,
serviceAccountName?: string,
imageName?: string,
}
31 changes: 31 additions & 0 deletions web/src/api/types/deployments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (C) 2023 by Posit Software, PBC.

import { Configuration } from 'src/api/types/configurations';
import { SchemaURL } from 'src/api/types/schema';

export type DeploymentError = {
error: string,
}

export enum ServerType {
CONNECT = 'connect',
SHINY_APPS = 'shinyapps',
CLOUD = 'cloud',
}

export type Deployment = {
$schema: SchemaURL,
serverType: ServerType
serverUrl: string,
id: string,
files: string[]
configurationPath: string
configurationName: string
configuration: Configuration
}

export function isDeploymentError(
d: Deployment | DeploymentError
): d is DeploymentError {
return (d as DeploymentError).error !== undefined;
}
3 changes: 3 additions & 0 deletions web/src/api/types/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Copyright (C) 2023 by Posit Software, PBC.

export type SchemaURL = string;
29 changes: 29 additions & 0 deletions web/src/components/ProjectPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,36 @@

<template>
<h1>Project Page</h1>

<h2>Destinations</h2>
<ul
v-for="deployment in deployments"
:key="deployment.id"
>
<li>
<RouterLink :to="`/destination/${deployment.id}`">
{{ deployment.serverUrl }}
</RouterLink>
</li>
</ul>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { RouterLink } from 'vue-router';

import { useApi } from 'src/api';
import { Deployment, isDeploymentError } from 'src/api/types/deployments';

const api = useApi();
const deployments = ref<Deployment[]>([]);

async function getDeployments() {
const response = (await api.deployments.getAll()).data;
deployments.value = response.filter<Deployment>((d): d is Deployment => {
return !isDeploymentError(d);
});
}

getDeployments();
</script>

0 comments on commit 2e06533

Please sign in to comment.