Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change implied 'save' and 'submit' endpoints to use common 'determineProjects' #48

Merged
merged 8 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@ CATALYST_SCRIPTS:=npx catalyst-scripts

LIQ_WORK_SRC:=src
TEST_STAGING:=test-staging

LIQ_WORK_FILES:=$(shell find $(LIQ_WORK_SRC) \( -name "*.js" -o -name "*.mjs" \) -not -path "*/test/*" -not -name "*.test.js")
LIQ_WORK_ALL_FILES:=$(shell find $(LIQ_WORK_SRC) \( -name "*.js" -o -name "*.mjs" \))
LIQ_WORK_TEST_SRC_FILES:=$(shell find $(LIQ_WORK_SRC) -name "*.js")
LIQ_WORK_TEST_BUILT_FILES:=$(patsubst $(LIQ_WORK_SRC)/%, test-staging/%, $(LIQ_WORK_TEST_SRC_FILES))

LIQ_WORK_TEST_SRC_DATA:=$(shell find $(LIQ_WORK_SRC) -path "*/test/data/*" -type f)
LIQ_WORK_TEST_BUILT_DATA:=$(patsubst $(LIQ_WORK_SRC)/%, test-staging/%, $(LIQ_WORK_TEST_SRC_DATA))
TEST_DATA_SRC=$(LIQ_WORK_SRC)/handlers/work/_lib/test/data
TEST_DATA_BUILT_SRC=$(patsubst $(LIQ_WORK_SRC)/%, $(TEST_STAGING)/%, $(TEST_DATA_SRC))

LIQ_WORK:=dist/liq-work.js

Expand All @@ -31,10 +35,12 @@ $(LIQ_WORK): package.json $(LIQ_WORK_FILES)
JS_SRC=$(LIQ_WORK_SRC) $(CATALYST_SCRIPTS) build

# test
$(LIQ_WORK_TEST_BUILT_DATA): $(TEST_STAGING)/%: $(LIQ_WORK_SRC)/%
@echo "Copying test data..."
@mkdir -p $(dir $@)
@cp $< $@
$(LIQ_WORK_TEST_BUILT_DATA) &: $(LIQ_WORK_TEST_SRC_DATA)
rm -rf $(TEST_DATA_BUILT_SRC)/*
mkdir -p $(TEST_DATA_BUILT_SRC)
cp -rf $(TEST_DATA_SRC)/* $(TEST_DATA_BUILT_SRC)
# we 'cp' so that when make compares the test-staging repos to the src repos, it doesn't see a lot of missing files
for DOT_GIT in $$(find $(TEST_DATA_BUILT_SRC) -name 'dot-git'); do mv $$DOT_GIT $$(dirname $$DOT_GIT)/.git; done

$(LIQ_WORK_TEST_BUILT_FILES) &: $(LIQ_WORK_ALL_FILES)
JS_SRC=$(LIQ_WORK_SRC) $(CATALYST_SCRIPTS) pretest
Expand All @@ -56,3 +62,9 @@ lint-fix:
JS_LINT_TARGET=$(LIQ_WORK_SRC) $(CATALYST_SCRIPTS) lint-fix

qa: test lint

test-repos-live:
for DG in $$(find $(LIQ_WORK_SRC) -name dot-git); do mv $$DG $$(dirname $$DG)/.git; done

test-repos-commitable:
for DG in $$(find $(LIQ_WORK_SRC) -name .git); do mv $$DG $$(dirname $$DG)/dot-git; done
14 changes: 14 additions & 0 deletions src/handlers/work/_lib/common-implied-parameters.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const getCommonImpliedParameters = ({ actionDesc }) => [
{
name : 'all',
isBoolean : true,
description : 'Saves all projects associated with the unit of work. This option overrides `projects`.'
},
{
name : 'projects',
isMultivalue : true,
description : `List of projects to ${actionDesc}. This option is ignored if \`all\` is specified.`
}
]

export { getCommonImpliedParameters }
45 changes: 45 additions & 0 deletions src/handlers/work/_lib/determine-projects.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import createError from 'http-errors'

import { determineCurrentBranch } from '@liquid-labs/git-toolkit'
import { determineImpliedProject } from '@liquid-labs/liq-projects-lib'

const determineProjects = async({ all, cliEndpoint, projects, reporter, req, workDB, workKey }) => {
// first, we determine the work key
const currDir = req.get('X-CWD')
if (workKey === undefined) {
requireCurrDir({ cliEndpoint, currDir })

workKey = await determineCurrentBranch({ projectPath : currDir, reporter })
}

const workUnit = workDB.requireData(workKey)

if (all === true) { // overrides anything other setting
projects = workUnit.projects.map((wu) => wu.name)
}
else if (projects === undefined) {
requireCurrDir({ cliEndpoint, currDir })

projects = [determineImpliedProject({ currDir })]
}
else { // else projects is defined, let's make sure they're valid
// remove duplicates in the list
projects = projects.filter((p, i, arr) => arr.indexOf(p) === i)

for (const projectFQN of projects) {
if (!workUnit.projects.find((p) => p.name === projectFQN)) {
throw createError.BadRequest(`No such project to save: '${projectFQN}'.`)
}
}
}

return [projects, workKey, workUnit]
}

const requireCurrDir = ({ cliEndpoint, currDir }) => {
if (currDir === undefined) {
throw createError.BadRequest(`Called '${cliEndpoint}' with implied work, but 'X-CWD' header not found.`)
}
}

export { determineProjects }
31 changes: 8 additions & 23 deletions src/handlers/work/_lib/save-lib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import { httpSmartResponse } from '@liquid-labs/http-smart-response'
import { tryExec } from '@liquid-labs/shell-toolkit'

import { getCommonImpliedParameters } from './common-implied-parameters'
import { determineProjects } from './determine-projects'
import { WorkDB } from './work-db'

const doSave = async({
Expand All @@ -26,32 +28,14 @@ const doSave = async({
res,
summary
}) => {
console.log('workKey:', workKey)
if (backupOnly !== true && summary === undefined) {
throw createError.BadRequest("You must specify 'summary' when saving local changes (committing).")
}

const cwd = req.get('X-CWD')
if (workKey === undefined) {
if (cwd === undefined) {
throw createError.BadRequest("Called 'work submit' with implied work, but 'X-CWD' header not found.")
}
const workDB = new WorkDB({ app, cache });

workKey = determineCurrentBranch({ projectPath : cwd, reporter })
}

const workDB = new WorkDB({ app, cache })
const workUnit = workDB.getData(workKey)
if (workUnit === undefined) {
throw createError.NotFound(`No such unit of work '${workKey}' found in work DB.`)
}

if (all === true) {
projects = workUnit.projects.map((wu) => wu.name)
}
else if (projects === undefined) {
projects = workUnit.projects.map((p) => p.name)
}
([projects, workKey] =
await determineProjects({ all, cliEndpoint : 'work save', projects, reporter, req, workDB, workKey }))

for (const projectFQN of projects) {
const [org, project] = projectFQN.split('/')
Expand Down Expand Up @@ -116,8 +100,9 @@ const getSaveEndpointParams = ({ descIntro }) => {
{
name : 'summary',
description : 'Short, concise description of the changes.'
}
]
},
...getCommonImpliedParameters({ actionDesc : 'save' })
].sort((a, b) => a.name.localeCompare(b.name))
}

Object.freeze(endpointParams.parameters)
Expand Down
29 changes: 8 additions & 21 deletions src/handlers/work/_lib/submit-lib.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import * as fsPath from 'node:path'

import createError from 'http-errors'

import { determineOriginAndMain, verifyBranchInSync, verifyClean, workBranchName } from '@liquid-labs/git-toolkit'
import { determineGitHubLogin } from '@liquid-labs/github-toolkit'
import { httpSmartResponse } from '@liquid-labs/http-smart-response'
Expand All @@ -11,9 +9,10 @@ import { Octocache } from '@liquid-labs/octocache'
import { tryExec } from '@liquid-labs/shell-toolkit'

import { GH_BASE_URL, WORKSPACE } from './constants'
import { determineProjects } from './determine-projects'
import { WorkDB } from './work-db'

const doSubmit = async({ app, cache, workKey, reporter, req, res }) => {
const doSubmit = async({ all, app, cache, projects, reporter, req, res, workKey }) => {
reporter = reporter.isolate()

const { dirtyOK, noPush = false } = req.vars
Expand All @@ -23,31 +22,19 @@ const doSubmit = async({ app, cache, workKey, reporter, req, res }) => {

const workDB = new WorkDB({ app, reporter }) // doesn't need auth token

const workUnit = workDB.getData(workKey)
if (workUnit === undefined) throw createError.NotFound(`No such active unit of work '${workKey}' found.`)
let workUnit;
([projects, workKey, workUnit] =
await determineProjects({ all, cliEndpoint : 'work submit', projects, reporter, req, workDB, workKey }))

let { assignees, closes, closeTarget, noBrowse = false, noCloses = false, projects } = req.vars
let { assignees, closes, closeTarget, noBrowse = false, noCloses = false } = req.vars

// determine assignee(s)
if (assignees === undefined) {
assignees = [(await determineGitHubLogin({ authToken })).login]
}

// determine the projects to submit
if (projects === undefined) {
projects = workUnit.projects
}
else {
// remove duplicates in the list
projects = projects.filter((p, i, arr) => i === arr.indexOf(p))
projects.forEach((p, i, arr) => {
const project = workUnit.projects.find((wup) => wup.name === p)
if (project === undefined) { throw createError.NotFound(`No record of project <em>${p}<rst> in unit of work '${workKey}'.`) }

arr.splice(i, 1, project)
})
}
// projects is now an array of project entries ({ name, private })
// map projects to array of project entries ({ name, private })
projects = projects.map((p) => workUnit.projects.find((wup) => wup.name === p))

// we can now check if we are closing issues and which issues to close
// because we de-duped, the lists would have equiv length our working set named all
Expand Down
1 change: 1 addition & 0 deletions src/handlers/work/_lib/test/data/playground/orgA/proj1
Submodule proj1 added at 4805e7
34 changes: 34 additions & 0 deletions src/handlers/work/_lib/test/determine-projects.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* global describe expect test */
import * as fsPath from 'node:path'

import { determineProjects } from '../determine-projects'

describe('determineProjects', () => {
const projectPath = fsPath.join(__dirname, 'data', 'playground', 'orgA', 'proj1')
const req = { get : (header) => header === 'X-CWD' ? projectPath : undefined }
const mockWorkUnit = {
projects : [{ name : 'orgA/proj1', private : true }]
}
const workDB = {
requireData : (key) => key === 'orgA/proj1/1' ? mockWorkUnit : throw new Error(`Unexpected: ${key}`)
}

test.each([
[false, 'orgA/proj1/1', undefined, ['orgA/proj1']],
[true, undefined, undefined, ['orgA/proj1']],
[false, undefined, ['orgA/proj1'], ['orgA/proj1']],
[true, undefined, ['orgA/proj2'], ['orgA/proj1']],
[false, 'orgA/proj1/1', ['orgA/proj1'], ['orgA/proj1']]
])('(all: %p, workKey: %s, projects: %p) -> %p', async(all, workKey, projects, expectedResult) => {
const [selectedProjects, workKeyOut, workUnit] =
await determineProjects({ all, cliEndpoint : 'test', projects, req, workDB, workKey })

expect(selectedProjects).toEqual(expectedResult)
expect(workKeyOut).toBe('orgA/proj1/1')
expect(workUnit).toEqual(mockWorkUnit)
})

test('throws with bad work key', () => {
expect(() => determineProjects({ cliEndpoint : 'test', req, workDB, workKey : 'orgA/blah-blah-blah/1' }))
})
})
9 changes: 9 additions & 0 deletions src/handlers/work/_lib/work-db.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,15 @@ const WorkDB = class WorkDB {
return structuredClone(workData)
}

requireData(workKey) {
const workUnit = this.getData(workKey)
if (workUnit === undefined) {
throw createError.NotFound(`No such unit of work '${workKey}'.`)
}

return workUnit
}

async #setupWorkBranches({ projects, reporter, workBranch }) {
const octocache = new Octocache({ authToken : this.#authToken })
for (const project of projects) {
Expand Down
5 changes: 2 additions & 3 deletions src/handlers/work/save-implied.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ const { help, method, parameters } = getSaveEndpointParams({ descIntro : 'Saves
const path = ['work', 'save']

const func = ({ app, cache, model, reporter }) => async(req, res) => {
const { all = false, backupOnly = false, description, noBackup = false, summary } = req.vars
const { projects } = req.vars
const { all = false, backupOnly = false, description, noBackup = false, projects, summary } = req.vars

doSave({ all, app, backupOnly, cache, description, noBackup, projects, reporter, req, res, summary })
await doSave({ all, app, backupOnly, cache, description, noBackup, projects, reporter, req, res, summary })
}

export { func, help, parameters, path, method }
5 changes: 2 additions & 3 deletions src/handlers/work/save.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ const { help, method, parameters } = getSaveEndpointParams({ descIntro : 'Saves
const path = ['work', ':workKey', 'save']

const func = ({ app, cache, model, reporter }) => async(req, res) => {
const { backupOnly = false, description, noBackup = false, summary, workKey } = req.vars
const { projects } = req.vars
const { backupOnly = false, description, noBackup = false, projects, summary, workKey } = req.vars

doSave({ app, backupOnly, cache, description, noBackup, projects, workKey, reporter, req, res, summary })
await doSave({ app, backupOnly, cache, description, noBackup, projects, workKey, reporter, req, res, summary })
}

export { func, help, parameters, path, method }
13 changes: 6 additions & 7 deletions src/handlers/work/submit-implied.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@ import createError from 'http-errors'

import { determineCurrentBranch } from '@liquid-labs/git-toolkit'

import { getCommonImpliedParameters } from './_lib/common-implied-parameters'
import { getSubmitEndpointParams, doSubmit } from './_lib/submit-lib'

let { help, method, parameters } = getSubmitEndpointParams({ descIntro : 'Submits the changes associated with the current unit of work by creating a pull request for the changes in each project associated with the unit of work.' })
parameters = [
{
name : 'all',
isBoolean : true,
description : 'Saves all projects associated with the unit of work.'
},
...getCommonImpliedParameters({ actionDesc : 'submit' }),
...parameters
]
].sort((a, b) => a.name.localeCompare(b.name))

const path = ['work', 'submit']

Expand All @@ -22,7 +19,9 @@ const func = ({ app, cache, model, reporter }) => async(req, res) => {

const workKey = determineCurrentBranch({ projectPath : cwd, reporter })

await doSubmit({ app, cache, workKey, reporter, req, res })
const { all, projects } = req.vars

await doSubmit({ all, app, cache, projects, reporter, req, res, workKey })
}

export { func, help, parameters, path, method }
4 changes: 2 additions & 2 deletions src/handlers/work/submit.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ const { help, method, parameters } = getSubmitEndpointParams({ descIntro : 'Subm
const path = ['work', ':workKey', 'submit']

const func = ({ app, cache, model, reporter }) => async(req, res) => {
const { workKey } = req.vars
const { projects, workKey } = req.vars

await doSubmit({ app, cache, workKey, reporter, req, res })
await doSubmit({ all : false, app, cache, projects, reporter, req, res, workKey })
}

export { func, help, parameters, path, method }