Files
cypress/packages/server/lib/project_static.ts
2021-09-29 14:34:01 -05:00

211 lines
5.4 KiB
TypeScript

import Debug from 'debug'
import commitInfo from '@cypress/commit-info'
import _ from 'lodash'
import logger from './logger'
import api from './api'
import cache from './cache'
import user from './user'
import keys from './util/keys'
import * as settings from './util/settings'
import { ProjectBase } from './project-base'
import { getDefaultConfigFilePath } from './project_utils'
const debug = Debug('cypress:server:project_static')
export async function getOrgs () {
const authToken = await user.ensureAuthToken()
return api.getOrgs(authToken)
}
export function paths () {
return cache.getProjectRoots()
}
export async function getPathsAndIds () {
const projectRoots: string[] = await cache.getProjectRoots()
// this assumes that the configFile for a cached project is 'cypress.json'
// https://git.io/JeGyF
return Promise.all(projectRoots.map(async (projectRoot) => {
return {
path: projectRoot,
id: await settings.id(projectRoot),
}
}))
}
export async function getDashboardProjects () {
const authToken = await user.ensureAuthToken()
debug('got auth token: %o', { authToken: keys.hide(authToken) })
return api.getProjects(authToken)
}
export function _mergeDetails (clientProject, project) {
return _.extend({}, clientProject, project, { state: 'VALID' })
}
export function _mergeState (clientProject, state) {
return _.extend({}, clientProject, { state })
}
export async function _getProject (clientProject, authToken) {
debug('get project from api', clientProject.id, clientProject.path)
try {
const project = await api.getProject(clientProject.id, authToken)
debug('got project from api')
return _mergeDetails(clientProject, project)
} catch (err: any) {
debug('failed to get project from api', err.statusCode)
switch (err.statusCode) {
case 404:
// project doesn't exist
return _mergeState(clientProject, 'INVALID')
case 403:
// project exists, but user isn't authorized for it
return _mergeState(clientProject, 'UNAUTHORIZED')
default:
throw err
}
}
}
export async function getProjectStatuses (clientProjects: any = []) {
debug(`get project statuses for ${clientProjects.length} projects`)
const authToken = await user.ensureAuthToken()
debug('got auth token: %o', { authToken: keys.hide(authToken) })
const projects = (await api.getProjects(authToken) || [])
debug(`got ${projects.length} projects`)
const projectsIndex = _.keyBy(projects, 'id')
return Promise.all(_.map(clientProjects, (clientProject) => {
debug('looking at', clientProject.path)
// not a CI project, just mark as valid and return
if (!clientProject.id) {
debug('no project id')
return _mergeState(clientProject, 'VALID')
}
const project = projectsIndex[clientProject.id]
if (project) {
debug('found matching:', project)
// merge in details for matching project
return _mergeDetails(clientProject, project)
}
debug('did not find matching:', project)
// project has id, but no matching project found
// check if it doesn't exist or if user isn't authorized
return _getProject(clientProject, authToken)
}))
}
export async function getProjectStatus (clientProject) {
debug('get project status for client id %s at path %s', clientProject.id, clientProject.path)
if (!clientProject.id) {
debug('no project id')
return Promise.resolve(_mergeState(clientProject, 'VALID'))
}
const authToken = await user.ensureAuthToken()
debug('got auth token: %o', { authToken: keys.hide(authToken) })
return _getProject(clientProject, authToken)
}
export function remove (path) {
return cache.removeProject(path)
}
export async function add (path, options) {
// don't cache a project if a non-default configFile is set
// https://git.io/JeGyF
if (settings.configFile(options) !== 'cypress.json') {
return Promise.resolve({ path })
}
try {
await cache.insertProject(path)
const id = await getId(path)
return {
id,
path,
}
} catch (e) {
return { path }
}
}
export async function getId (path) {
const configFile = await getDefaultConfigFilePath(path)
return new ProjectBase({ projectRoot: path, testingType: 'e2e', options: { configFile } }).getProjectId()
}
interface ProjectIdOptions{
id: string
projectRoot: string
configFile: string
}
export async function writeProjectId ({ id, projectRoot, configFile }: ProjectIdOptions) {
const attrs = { projectId: id }
logger.info('Writing Project ID', _.clone(attrs))
// TODO: We need to set this
// this.generatedProjectIdTimestamp = new Date()
await settings.write(projectRoot, attrs, { configFile })
return id
}
interface ProjectDetails {
projectName: string
projectRoot: string
orgId: string | null
public: boolean
configFile: string
}
export async function createCiProject ({ projectRoot, configFile, ...projectDetails }: ProjectDetails) {
debug('create CI project with projectDetails %o projectRoot %s', projectDetails)
const authToken = await user.ensureAuthToken()
const remoteOrigin = await commitInfo.getRemoteOrigin(projectRoot)
debug('found remote origin at projectRoot %o', {
remoteOrigin,
projectRoot,
})
const newProject = await api.createProject(projectDetails, remoteOrigin, authToken)
await writeProjectId({
configFile,
projectRoot,
id: newProject.id,
})
return newProject
}