Skip to content

Commit 6e39cb8

Browse files
authored
feat: refactor getSite to base-command (#5743)
* feat: add hook, convert functions-list * fix: clarify names * feat: refactor deploy command, add search by name to base command * feat: convert link * feat: convert open-admin * feat: convert open:site * feat: error out on getSiteByName * fix: format * feat: convert sites:delete * fix: undo convert sites:delete as it was wrong :D * feat: convert status:hooks * chore: format * feat: convert unlink * feat: return error instead of just calling error * fix: throw error if undefined
1 parent 0d7d996 commit 6e39cb8

File tree

10 files changed

+106
-160
lines changed

10 files changed

+106
-160
lines changed

src/commands/base-command.mjs

+17-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
warn,
2525
} from '../utils/command-helpers.mjs'
2626
import getGlobalConfig from '../utils/get-global-config.mjs'
27+
import { getSiteByName } from '../utils/get-site.mjs'
2728
import openBrowser from '../utils/open-browser.mjs'
2829
import StateConfig from '../utils/state-config.mjs'
2930
import { identify, track } from '../utils/telemetry/index.mjs'
@@ -440,11 +441,24 @@ export default class BaseCommand extends Command {
440441
certificateFile: options.httpProxyCertificateFilename,
441442
})
442443
const apiOpts = { ...apiUrlOpts, agent }
444+
const api = new NetlifyAPI(token || '', apiOpts)
445+
446+
// If a user passes a site name as an option instead of a site ID to options.site, the siteInfo object
447+
// will only have the property siteInfo.id. Checking for one of the other properties ensures that we can do
448+
// a re-call of the api.getSite() that is done in @netlify/config so we have the proper site object in all
449+
// commands.
450+
// options.site as a site name (and not just site id) was introduced for the deploy command, so users could
451+
// deploy by name along with by id
452+
let siteData = siteInfo
453+
if (!siteData.url && options.site) {
454+
siteData = await getSiteByName(api, options.site)
455+
}
456+
443457
const globalConfig = await getGlobalConfig()
444458

445459
actionCommand.netlify = {
446460
// api methods
447-
api: new NetlifyAPI(token || '', apiOpts),
461+
api,
448462
apiOpts,
449463
repositoryRoot,
450464
// current site context
@@ -458,8 +472,8 @@ export default class BaseCommand extends Command {
458472
state.set('siteId', id)
459473
},
460474
},
461-
// Site information retrieved using the API
462-
siteInfo,
475+
// Site information retrieved using the API (api.getSite())
476+
siteInfo: siteData,
463477
// Configuration from netlify.[toml/yml]
464478
config: normalizedConfig,
465479
// Used to avoid calling @netlify/config again

src/commands/deploy/deploy.mjs

+6-28
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { runCoreSteps } from '@netlify/build'
77
import { restoreConfig, updateConfig } from '@netlify/config'
88
import { Option } from 'commander'
99
import inquirer from 'inquirer'
10+
import isEmpty from 'lodash/isEmpty.js'
1011
import isObject from 'lodash/isObject.js'
1112
import prettyjson from 'prettyjson'
1213

@@ -488,7 +489,7 @@ const printResults = ({ deployToProduction, json, results, runBuildCommand }) =>
488489
* @param {import('../base-command.mjs').default} command
489490
*/
490491
const deploy = async (options, command) => {
491-
const { api, site } = command.netlify
492+
const { api, site, siteInfo } = command.netlify
492493
const alias = options.alias || options.branch
493494

494495
command.setAnalyticsPayload({ open: options.open, prod: options.prod, json: options.json, alias: Boolean(alias) })
@@ -503,35 +504,12 @@ const deploy = async (options, command) => {
503504

504505
await command.authenticate(options.auth)
505506

506-
let siteId = options.site || site.id
507+
let siteId = site.id || options.site
507508

508509
let siteData = {}
509-
if (siteId) {
510-
try {
511-
const [{ siteError, siteFoundById }, sites] = await Promise.all([
512-
api
513-
.getSite({ siteId })
514-
.then((data) => ({ siteFoundById: data }))
515-
.catch((error_) => ({ siteError: error_ })),
516-
api.listSites({ name: options.site, filter: 'all' }),
517-
])
518-
const siteFoundByName = sites.find((filteredSite) => filteredSite.name === options.site)
519-
if (siteFoundById) {
520-
siteData = siteFoundById
521-
} else if (siteFoundByName) {
522-
siteData = siteFoundByName
523-
siteId = siteFoundByName.id
524-
} else {
525-
throw siteError
526-
}
527-
} catch (error_) {
528-
// TODO specifically handle known cases (e.g. no account access)
529-
if (error_.status === 404) {
530-
error('Site not found')
531-
} else {
532-
error(error_.message)
533-
}
534-
}
510+
if (siteId && !isEmpty(siteInfo)) {
511+
siteData = siteInfo
512+
siteId = siteData.id
535513
} else {
536514
log("This folder isn't linked to a site yet")
537515
const NEW_SITE = '+ Create & configure a new site'

src/commands/functions/functions-list.mjs

+5-25
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// @ts-check
22
import AsciiTable from 'ascii-table'
33

4-
import { error, exit, log, logJson, warn } from '../../utils/command-helpers.mjs'
4+
import { exit, log, logJson } from '../../utils/command-helpers.mjs'
55
import { getFunctions, getFunctionsDir } from '../../utils/functions/index.mjs'
6+
import requiresSiteInfo from '../../utils/hooks/requires-site-info.mjs'
67

78
const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
89
const isDeployed = deployedFunctions.some((deployedFunction) => deployedFunction.n === name)
@@ -15,31 +16,9 @@ const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
1516
* @param {import('../base-command.mjs').default} command
1617
*/
1718
const functionsList = async (options, command) => {
18-
const { api, config, site } = command.netlify
19+
const { config, siteInfo } = command.netlify
1920

20-
// get deployed site details
21-
// copied from `netlify status`
22-
const siteId = site.id
23-
if (!siteId) {
24-
warn('Did you run `netlify link` yet?')
25-
error(`You don't appear to be in a folder that is linked to a site`)
26-
}
27-
let siteData
28-
try {
29-
siteData = await api.getSite({ siteId })
30-
} catch (error_) {
31-
// unauthorized
32-
if (error_.status === 401) {
33-
warn(`Log in with a different account or re-link to a site you have permission for`)
34-
error(`Not authorized to view the currently linked site (${siteId})`)
35-
}
36-
// missing
37-
if (error_.status === 404) {
38-
error(`The site this folder is linked to can't be found`)
39-
}
40-
error(error_)
41-
}
42-
const deploy = siteData.published_deploy || {}
21+
const deploy = siteInfo.published_deploy || {}
4322
const deployedFunctions = deploy.available_functions || []
4423

4524
const functionsDir = getFunctionsDir({ options, config })
@@ -91,4 +70,5 @@ NOT the same as listing the functions that have been deployed. For that info you
9170
)
9271
.option('-f, --functions <dir>', 'Specify a functions directory to list')
9372
.option('--json', 'Output function data as JSON')
73+
.hook('preAction', requiresSiteInfo)
9474
.action(functionsList)

src/commands/link/link.mjs

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-check
22
import { Option } from 'commander'
33
import inquirer from 'inquirer'
4+
import isEmpty from 'lodash/isEmpty.js'
45

56
import { listSites } from '../../lib/api.mjs'
67
import { chalk, error, exit, log } from '../../utils/command-helpers.mjs'
@@ -250,28 +251,23 @@ export const link = async (options, command) => {
250251
api,
251252
repositoryRoot,
252253
site: { id: siteId },
254+
siteInfo,
253255
state,
254256
} = command.netlify
255257

256-
let siteData
257-
try {
258-
// @ts-ignore types from API are wrong they cannot recognize `getSite` of API
259-
siteData = await api.getSite({ siteId })
260-
} catch {
261-
// silent api error
262-
}
258+
let siteData = siteInfo
263259

264260
// Add .netlify to .gitignore file
265261
await ensureNetlifyIgnore(repositoryRoot)
266262

267263
// Site id is incorrect
268-
if (siteId && !siteData) {
264+
if (siteId && isEmpty(siteData)) {
269265
log(`"${siteId}" was not found in your Netlify account.`)
270266
log(`Please double check your siteID and which account you are logged into via \`netlify status\`.`)
271267
return exit()
272268
}
273269

274-
if (siteData) {
270+
if (!isEmpty(siteInfo)) {
275271
// If already linked to site. exit and prompt for unlink
276272
log(`Site already linked to "${siteData.name}"`)
277273
log(`Admin url: ${siteData.admin_url}`)

src/commands/open/open-admin.mjs

+7-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { error, exit, log, warn } from '../../utils/command-helpers.mjs'
1+
import { exit, log } from '../../utils/command-helpers.mjs'
2+
import requiresSiteInfo from '../../utils/hooks/requires-site-info.mjs'
23
import openBrowser from '../../utils/open-browser.mjs'
34

45
/**
@@ -7,42 +8,14 @@ import openBrowser from '../../utils/open-browser.mjs'
78
* @param {import('../base-command.mjs').default} command
89
*/
910
export const openAdmin = async (options, command) => {
10-
const { api, site } = command.netlify
11+
const { siteInfo } = command.netlify
1112

1213
await command.authenticate()
1314

14-
const siteId = site.id
15+
log(`Opening "${siteInfo.name}" site admin UI:`)
16+
log(`> ${siteInfo.admin_url}`)
1517

16-
if (!siteId) {
17-
warn(`No Site ID found in current directory.
18-
Run \`netlify link\` to connect to this folder to a site`)
19-
return false
20-
}
21-
22-
let siteData
23-
try {
24-
siteData = await api.getSite({ siteId })
25-
log(`Opening "${siteData.name}" site admin UI:`)
26-
log(`> ${siteData.admin_url}`)
27-
} catch (error_) {
28-
// unauthorized
29-
if (error_.status === 401) {
30-
warn(`Log in with a different account or re-link to a site you have permission for`)
31-
error(`Not authorized to view the currently linked site (${siteId})`)
32-
}
33-
// site not found
34-
if (error_.status === 404) {
35-
log()
36-
log('Please double check this ID and verify you are logged in with the correct account')
37-
log()
38-
log('To fix this, run `netlify unlink` then `netlify link` to reconnect to the correct site ID')
39-
log()
40-
error(`Site "${siteId}" not found in account`)
41-
}
42-
error(error_)
43-
}
44-
45-
await openBrowser({ url: siteData.admin_url })
18+
await openBrowser({ url: siteInfo.admin_url })
4619
exit()
4720
}
4821

@@ -56,4 +29,5 @@ export const createOpenAdminCommand = (program) =>
5629
.command('open:admin')
5730
.description('Opens current site admin UI in Netlify')
5831
.addExamples(['netlify open:admin'])
32+
.hook('preAction', requiresSiteInfo)
5933
.action(openAdmin)

src/commands/open/open-site.mjs

+7-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { error, exit, log, warn } from '../../utils/command-helpers.mjs'
1+
import { exit, log } from '../../utils/command-helpers.mjs'
2+
import requiresSiteInfo from '../../utils/hooks/requires-site-info.mjs'
23
import openBrowser from '../../utils/open-browser.mjs'
34

45
/**
@@ -7,33 +8,13 @@ import openBrowser from '../../utils/open-browser.mjs'
78
* @param {import('../base-command.mjs').default} command
89
*/
910
export const openSite = async (options, command) => {
10-
const { api, site } = command.netlify
11+
const { siteInfo } = command.netlify
1112

1213
await command.authenticate()
1314

14-
const siteId = site.id
15-
16-
if (!siteId) {
17-
warn(`No Site ID found in current directory.
18-
Run \`netlify link\` to connect to this folder to a site`)
19-
return false
20-
}
21-
22-
let siteData
23-
let url
24-
try {
25-
siteData = await api.getSite({ siteId })
26-
url = siteData.ssl_url || siteData.url
27-
log(`Opening "${siteData.name}" site url:`)
28-
log(`> ${url}`)
29-
} catch (error_) {
30-
// unauthorized
31-
if (error_.status === 401) {
32-
warn(`Log in with a different account or re-link to a site you have permission for`)
33-
error(`Not authorized to view the currently linked site (${siteId})`)
34-
}
35-
error(error_)
36-
}
15+
const url = siteInfo.ssl_url || siteInfo.url
16+
log(`Opening "${siteInfo.name}" site url:`)
17+
log(`> ${url}`)
3718

3819
await openBrowser({ url })
3920
exit()
@@ -49,4 +30,5 @@ export const createOpenSiteCommand = (program) =>
4930
.command('open:site')
5031
.description('Opens current site url in browser')
5132
.addExamples(['netlify open:site'])
33+
.hook('preAction', requiresSiteInfo)
5234
.action(openSite)

src/commands/status/status-hooks.mjs

+12-29
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,22 @@
11
// @ts-check
22
import prettyjson from 'prettyjson'
33

4-
import { error, log, warn } from '../../utils/command-helpers.mjs'
4+
import { log } from '../../utils/command-helpers.mjs'
5+
import requiresSiteInfo from '../../utils/hooks/requires-site-info.mjs'
56

67
/**
78
* The status:hooks command
89
* @param {import('commander').OptionValues} options
910
* @param {import('../base-command.mjs').default} command
1011
*/
1112
const statusHooks = async (options, command) => {
12-
const { api, site } = command.netlify
13+
const { api, siteInfo } = command.netlify
1314

1415
await command.authenticate()
1516

16-
const siteId = site.id
17-
if (!siteId) {
18-
warn('Did you run `netlify link` yet?')
19-
error(`You don't appear to be in a folder that is linked to a site`)
20-
}
21-
22-
let siteData
23-
try {
24-
siteData = await api.getSite({ siteId })
25-
} catch (error_) {
26-
// unauthorized
27-
if (error_.status === 401) {
28-
warn(`Log in with a different account or re-link to a site you have permission for`)
29-
error(`Not authorized to view the currently linked site (${siteId})`)
30-
}
31-
// missing
32-
if (error_.status === 404) {
33-
error(`The site this folder is linked to can't be found`)
34-
}
35-
error(error_)
36-
}
37-
38-
const ntlHooks = await api.listHooksBySiteId({ siteId: siteData.id })
17+
const ntlHooks = await api.listHooksBySiteId({ siteId: siteInfo.id })
3918
const data = {
40-
site: siteData.name,
19+
site: siteInfo.name,
4120
hooks: {},
4221
}
4322
ntlHooks.forEach((hook) => {
@@ -47,8 +26,8 @@ const statusHooks = async (options, command) => {
4726
id: hook.id,
4827
disabled: hook.disabled,
4928
}
50-
if (siteData.build_settings?.repo_url) {
51-
data.hooks[hook.id].repo_url = siteData.build_settings.repo_url
29+
if (siteInfo.build_settings?.repo_url) {
30+
data.hooks[hook.id].repo_url = siteInfo.build_settings.repo_url
5231
}
5332
})
5433
log(`─────────────────┐
@@ -63,4 +42,8 @@ Site Hook Status │
6342
* @returns
6443
*/
6544
export const createStatusHooksCommand = (program) =>
66-
program.command('status:hooks').description('Print hook information of the linked site').action(statusHooks)
45+
program
46+
.command('status:hooks')
47+
.description('Print hook information of the linked site')
48+
.hook('preAction', requiresSiteInfo)
49+
.action(statusHooks)

src/commands/unlink/unlink.mjs

+2-8
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,15 @@ import { track } from '../../utils/telemetry/index.mjs'
88
* @param {import('../base-command.mjs').default} command
99
*/
1010
const unlink = async (options, command) => {
11-
const { site, state } = command.netlify
11+
const { site, siteInfo, state } = command.netlify
1212
const siteId = site.id
1313

1414
if (!siteId) {
1515
log(`Folder is not linked to a Netlify site. Run 'netlify link' to link it`)
1616
return exit()
1717
}
1818

19-
let siteData = {}
20-
try {
21-
// @ts-ignore types from API are wrong they cannot recognize `getSite` of API
22-
siteData = await command.netlify.api.getSite({ siteId })
23-
} catch {
24-
// ignore errors if we can't get the site
25-
}
19+
const siteData = siteInfo
2620

2721
state.delete('siteId')
2822

0 commit comments

Comments
 (0)