@suffolklitlab/alkiln 5.13.3 → 5.13.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/github_server.yml +7 -7
- package/.github/workflows/playground.yml +1 -1
- package/CHANGELOG.md +24 -0
- package/action.yml +70 -28
- package/action_for_github_server/action.yml +61 -29
- package/docs/4_environments.md +243 -0
- package/docs/artifacts_folder_name.md +135 -0
- package/docs/constrained_random_tests.md +320 -0
- package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
- package/docs/decisions/007_getting_and_storing_logs.md +179 -0
- package/docs/decisions/move_runtime_config_to_sources_folder.md +3 -1
- package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
- package/docs/kinds_of_errors.md +26 -0
- package/lib/docassemble/docassemble_api_interface.js +77 -51
- package/lib/docassemble/server_install.js +32 -9
- package/lib/docassemble/setup.js +40 -25
- package/lib/docassemble/takedown.js +33 -10
- package/lib/parse.js +36 -0
- package/lib/run_cucumber.js +115 -36
- package/lib/scope.js +60 -75
- package/lib/steps.js +70 -42
- package/lib/utils/Log.js +982 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_sources_paths.js +162 -0
- package/lib/utils/parse.js +1 -0
- package/lib/utils/proccess_command_line_args.js +267 -0
- package/lib/utils/reports.js +64 -17
- package/lib/utils/session_vars.js +34 -21
- package/lib/utils/time.js +44 -0
- package/package.json +1 -1
- package/tests/log_codes/check_codes.sh +2 -1
- package/tests/log_codes/log_code_expected_instances.txt +33 -9
- package/tests/unit_tests/getAllFields.test.js +5 -0
- package/tests/unit_tests/getMatchingRows.test.js +5 -0
- package/tests/unit_tests/log.test.js +581 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/lib/utils/log.js +0 -96
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Shape and behavior of the report and log objects
|
|
2
|
+
|
|
3
|
+
<!-- TODO: turn this into a architecture design doc? -->
|
|
4
|
+
<!-- TODO: Rename to just "logs" -->
|
|
5
|
+
|
|
6
|
+
## Context and scope
|
|
7
|
+
|
|
8
|
+
We show logs to the user in the console and as test suite artifacts. We have logs we use for development. cucumberjs also has logs we want to include in the above until we are confident we are handling all cases ourselves.
|
|
9
|
+
|
|
10
|
+
Right now, the system for logging is fractured, scattered, and duplicated. Logs that are not from the report don't get saved anywhere. Some logs only get shown in debug mode in the console, which can be frustrating and unhelpful. We lack verbose output information for tests that authors run in the wild.
|
|
11
|
+
|
|
12
|
+
This document excludes discussion of report architecture, but it is useful to note that at the moment they are completely separate from logs and yet save to some of the same files, so they influence our current choices.
|
|
13
|
+
|
|
14
|
+
## Goals
|
|
15
|
+
|
|
16
|
+
- Make log information persistent and visible
|
|
17
|
+
- Add additional meaningful information to logs (e.g. metadata)
|
|
18
|
+
- Make sure logs are clear without needing to read the surrounding code
|
|
19
|
+
- Unify and clarify log flow
|
|
20
|
+
- Make log behavior consistent
|
|
21
|
+
|
|
22
|
+
## Non/anti-goals
|
|
23
|
+
|
|
24
|
+
- Disrupt the signals we're giving authors about their tests with the noise of internal ALKiln details
|
|
25
|
+
- Create a complex and heavy system to try to abstract absolutely everything
|
|
26
|
+
- Let the complexity of the relationship with report behavior too heavily influencing our log design goals
|
|
27
|
+
|
|
28
|
+
## The actual design
|
|
29
|
+
|
|
30
|
+
### `Log` internal errors
|
|
31
|
+
|
|
32
|
+
Logs are informational and not related to the behavior of authors' tests. Because of that, these logs should avoid adding noise to what an author sees. That is, internal warnings or errors should look different than authors' test errors. To help with this:
|
|
33
|
+
|
|
34
|
+
- All log methods` arguments have default values.
|
|
35
|
+
- We use try/catch very aggressively in the `Log` class.
|
|
36
|
+
|
|
37
|
+
Read more of the thoughts behind this choice in the decision doc on which log level to pick and the design doc on types of errors.
|
|
38
|
+
|
|
39
|
+
### Purposes of methods
|
|
40
|
+
|
|
41
|
+
Some of these notes have to do with reports because logs and reports are using some of the same files at the moment and yet are not integrated. We'll write a doc for reports when we integrate reports more fully with logs.
|
|
42
|
+
|
|
43
|
+
1. `Log`: Messages or errors from wrapping functionality, like setup and takedown messages.
|
|
44
|
+
2. `Log`: Test progress (a compact way to see what's happening/happened in each test):
|
|
45
|
+
|
|
46
|
+
Test1: .............F--.
|
|
47
|
+
|
|
48
|
+
Test 2: ....
|
|
49
|
+
3. Report: A pretty report at the end with details about each test. It has sections, like "failed" and "passed", headings, etc.
|
|
50
|
+
4. Report: cucumberjs's various console logs.
|
|
51
|
+
5. `Log`: Lots of other informational logs for internal development and debugging purposes.
|
|
52
|
+
|
|
53
|
+
Different logs belong in different places:
|
|
54
|
+
|
|
55
|
+
- ALKiln always logs items 1, 2, 3, and 4 to the console.
|
|
56
|
+
- Ideally save all items in a verbose debug file sequentially as the tests run.
|
|
57
|
+
- During the tests, save items 2, 3, and 4 to a running report file sequentially as they come. Include metadata. Suggested filenames: `report_log.txt`/`running_report.txt`/`report_run.txt`. If we have to stop the tests early because of an infinite loop or something, this will at least show the test author some information.
|
|
58
|
+
- At the end of the tests, format items 1, 2, 3, and 4 nicely in a file called `report.txt`. This will mostly exclude the metadata.
|
|
59
|
+
- When there is a warning-level or error-level log, save 1, 3, 4, and 5 in an unexpected results file too.
|
|
60
|
+
- In future, discuss the usefulness of having a debug mode where ALKiln logs all items to the console while the tests are running.
|
|
61
|
+
|
|
62
|
+
We may also want copies of various files that have different formats for different environments (see docs on the different environments in which ALKiln runs).
|
|
63
|
+
|
|
64
|
+
- Console logs with colors.
|
|
65
|
+
- Plain text versions that exclude those color codes.
|
|
66
|
+
- HTML versions to show with, for example ALKilnInThePlayground. Maybe in GitHub as well in the future.
|
|
67
|
+
|
|
68
|
+
### Anatomy and behavior of logging methods
|
|
69
|
+
|
|
70
|
+
Most of a `Log`'s logging methods add additional metadata to a log:
|
|
71
|
+
|
|
72
|
+
1. The level of the log (success, info, warning, error, and custom values)
|
|
73
|
+
2. An icon showing the level of a log
|
|
74
|
+
3. A log code, like (e.g. ALK0105)
|
|
75
|
+
4. Contextual info about where the log originated (e.g. "setup", "takedown", "invalid answer")
|
|
76
|
+
9. A timestamp
|
|
77
|
+
|
|
78
|
+
To be able to show that metadata, most methods will accept custom values for parts of that metadata. Most methods offer:
|
|
79
|
+
|
|
80
|
+
- Log code
|
|
81
|
+
- Contextual info
|
|
82
|
+
- The error throwing information
|
|
83
|
+
|
|
84
|
+
For the debug method, everything other than the timestamp can be customized.
|
|
85
|
+
|
|
86
|
+
Callers can influence the content and formatting of most parts of the log message. For example, they can call `.warn` to show a warning icon and level metadata. The one exception would usually be the part before the metadata. Customizing that could be useful for things like creating a visual separator before ALKiln starts evaluating the fields of a web page. For that reason, the caller can add a `before` property to the metadata that a `Log` object will add before the metadata.
|
|
87
|
+
|
|
88
|
+
A `Log` object should be able to throw errors so that it can log metadata and any additional styling and context along with the error. It can also make sure that the error will be an actual `Error` object (as opposed to a string or other object). Otherwise everywhere we needed to throw an error, we would need duplicate handling bad data (like objects with circular references), using `Log` to save to files (etc.), throwing the error, and other possible complications. For that, `Logs` also need to accept this additional information:
|
|
89
|
+
|
|
90
|
+
- Whether to throw an error
|
|
91
|
+
- The error to throw
|
|
92
|
+
|
|
93
|
+
Those methods should also accept multiple individual log arguments the same way `console.log()` does. This lets the caller include strings separately from objects they want to log, including `Error` objects, so that a `Log` object can handle them safely and style them consistently. We could have required a list of logs instead, but the extra syntax was frustrating.
|
|
94
|
+
|
|
95
|
+
Right now, the only way we differentiate methods that just store data vs. methods that also log to the console is with documentation. This seems less than ideal. Using the name `record` could be confusing because developers might think they need to log to the console separately from saving the information to debug files.
|
|
96
|
+
|
|
97
|
+
## APIs
|
|
98
|
+
|
|
99
|
+
Proposed external methods: `success`, `info`, `warn`, `throw`, `stdout`, `debug`. See the `Log` class and its tests for the most up-to-date API.
|
|
100
|
+
|
|
101
|
+
<!-- Discuss: better name for `stdout`? -->
|
|
102
|
+
|
|
103
|
+
## Data storage
|
|
104
|
+
|
|
105
|
+
- `report.txt` - final pretty output for the user with headings, etc.
|
|
106
|
+
- Each Scenario's `report.txt` - pretty output in each test's folder.
|
|
107
|
+
- `report_log.txt` (name?) - ugly, but still sparse, giving the user some info about what happened during the tests without overwhelming them.
|
|
108
|
+
- `debug_log.txt` (name? `debug`?) - very ugly and full of all the information from everywhere else and more. Useful for internal development and troubleshooting. Includes setup, takedown, etc.
|
|
109
|
+
- `temp_unexpected_results_debug_log.txt` - A file containing messages about unexpected behavior, like test warnings and errors, as well as internal ALKiln warnings. We don't output this in our GitHub actions - the file itself is temporary, we just add its contents to the final unexpected results file. All its information exists in the debug file too.
|
|
110
|
+
- `unexpected_results.txt` - Logs for failures and other unexpected behavior. It has information about test warnings and errors, as well as internal ALKiln warnings. At first we only save report information there. When the tests finish, we add the contents of the specific debug file that contains only unexpected results.
|
|
111
|
+
|
|
112
|
+
## Degree of constraint
|
|
113
|
+
|
|
114
|
+
- Right now we're a bit limited by report functionality staying separate. Other than that, we can design as we want. We should avoid the current behavior of reports substantially influencing our design decisions here. We do have to keep in mind how reports might integrate with this in the future, but we will also need to change the design of the report behavior significantly in the future, so we should keep an open mind.
|
|
115
|
+
- We have not yet approached saving logs from GitHub actions, though this should be fairly simple. It may add some complication.
|
|
116
|
+
- We have not yet solved getting all logs from the console (e.g. cucumberjs logs).
|
|
117
|
+
|
|
118
|
+
## Alternatives considered
|
|
119
|
+
|
|
120
|
+
- Avoid the complexity of storing setup and takedown output in the verbose/debug file. In GitHub, they're in the GitHub console. Locally, we see the results immediately. On the other hand, storing them ourselves lets us look for information in just one place.
|
|
121
|
+
- Include GitHub action output in the debug files. Out of scope for the current iteration.
|
|
122
|
+
- Catch cucumberjs's actual console output. Out of scope for the current iteration.
|
|
123
|
+
|
|
124
|
+
### System-context diagram
|
|
125
|
+
|
|
126
|
+
Current design before integration of reports, GitHub action logs, and other non-ALKiln console logs.
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
## `Log`
|
|
130
|
+
|
|
131
|
+
console methods -> ._console() -> .debug() -> log to the console
|
|
132
|
+
.stdout() -> .debug() -> sometimes write to stdout
|
|
133
|
+
|
|
134
|
+
.debug() -> debug_log.txt with metadata
|
|
135
|
+
-> sometimes temp_unexpected_results_debug_log.txt with metadata
|
|
136
|
+
|
|
137
|
+
## `Report`
|
|
138
|
+
report and its methods -> report_log.txt (plain text)
|
|
139
|
+
-> debug_log.txt (plain text)
|
|
140
|
+
-> sometimes unexpected_results.txt (plain text)
|
|
141
|
+
-> report summary at end of tests
|
|
142
|
+
-> final report.txt (plain text summary)
|
|
143
|
+
-> final console log (summary with console colors)
|
|
144
|
+
-> individual Scenario reports at end of tests
|
|
145
|
+
-> final individual report summary file in each Scenario's folder
|
|
146
|
+
|
|
147
|
+
## takedown
|
|
148
|
+
Add temp_unexpected_results_debug_log.txt to the end of unexpected_results.txt
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Cross-cutting concerns
|
|
152
|
+
|
|
153
|
+
_Security, privacy, observability, etc._
|
|
154
|
+
|
|
155
|
+
Code that calls `Log` methods should avoid passing sensitive information. A `Log` object doesn't protect against exposing information.
|
|
156
|
+
|
|
157
|
+
## Related documentation
|
|
158
|
+
|
|
159
|
+
Decision doc about which log level to use.
|
|
160
|
+
Architecture design doc about types of unexpected and expected "errors".
|
|
161
|
+
Decision doc about getting and storing logs.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Kinds of Errors
|
|
2
|
+
|
|
3
|
+
An error, or even an `Error` instance, can mean a lot of different things in the context of this testing framework. Throwing errors for failing tests is what we are most familiar with, but it is just one kind of error.
|
|
4
|
+
|
|
5
|
+
## Interview `Error`s
|
|
6
|
+
|
|
7
|
+
1. **Interview bugs**: For example, the interview says ALKiln's answer is invalid. The author expected that answer to be valid. ALKiln saves an error message to the report and throws an error to tell cucumber to mark the test as failed. Currently, these logs errors come with the 🤕 icon. When all the tests are done, the test suite will show as failing in the console, the report, and the various logs. In future, ALKiln will use a method like `report.throw()` to save to the various log files with the `error` level and then throw the error to let cucumber know the test failed.
|
|
8
|
+
1. **Interview expected errors**: For example, the interview says ALKiln's answer is invalid. The author was testing their interview's field validation rules. The test passes.
|
|
9
|
+
|
|
10
|
+
## Test file `Error`s
|
|
11
|
+
|
|
12
|
+
1. **Test file syntax error**: For example, a row in a table in a test file has too many columns. cucumberjs throws an error and no tests are run. ALKiln should show the tests as failed. ALKiln is unable to get the output of that error into a log, but there should be an error in the console.
|
|
13
|
+
1. **Test Step typo**: For example, `And I ta p to contcontinue`. cucumberjs will cause this test to fail, giving an "undefined" Step as the reason. The test suite will show as failing in the console, the report, and the various logs.
|
|
14
|
+
1. **Test bugs**: For example, a test has old values that need to be updated. The test fails. This will look like an interview bug, as this document describes.
|
|
15
|
+
|
|
16
|
+
## ALKiln `Error`s
|
|
17
|
+
|
|
18
|
+
1. **ALKiln un-handled bugs**: For example, we have forgotten to define a variable and none of our tests has yet triggered that code. These are unexpected errors that force processes to abort or force individual tests to fail in uninformative ways. To an author that runs into that error, it looks like their test has failed, but the fault is ours and we need to troubleshoot it. We should make that clear.
|
|
19
|
+
1. **ALKiln silent bugs**: For example, a debug log has an object that causes an error when ALKiln tries to turn it into a string. ALKiln should record the failure somewhere, but should avoid throwing it. We can troubleshoot those if we see them and add new internal tests. See decisions docs on when to use which kind of log level.
|
|
20
|
+
1. **ALKiln expected errors**: For example, one promise won a `Promise.race()`, so ALKiln aborted the other promises. That causes those aborted promises to error. That is why ALKiln saves the "informational" Error to the debug files and it has a level of "info" or "debug".
|
|
21
|
+
|
|
22
|
+
## ALKiln's internal tests
|
|
23
|
+
|
|
24
|
+
1. **ALKiln internal success-handling test failures**: For example, the test fails to fill out radio buttons correctly.
|
|
25
|
+
1. **ALKiln internal failure-handling test successes**: For example, we test that the summary report shows a specific log code when an interview fails to load and the log code shows up correctly. The "test" fails - the interview fails to load - but *our* test passes - the right log code was in the summary report.
|
|
26
|
+
1. **ALKiln internal failure-handling test failures**: For example, we test that the report shows a specific log code when an interview fails to load, but the log code is missing. The "test" fails and *our* test fails.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const _da_REST = require('./docassemble_api_REST');
|
|
2
|
-
const
|
|
2
|
+
const Log = require('../utils/Log');
|
|
3
3
|
const session_vars = require('../utils/session_vars' );
|
|
4
4
|
const time = require('../utils/time');
|
|
5
5
|
|
|
@@ -61,6 +61,10 @@ const time = require('../utils/time');
|
|
|
61
61
|
let da_i = {};
|
|
62
62
|
module.exports = da_i;
|
|
63
63
|
|
|
64
|
+
const context = `da api`;
|
|
65
|
+
const artifacts_path = session_vars.get_artifacts_path_name();
|
|
66
|
+
const log = new Log({ path: artifacts_path, context });
|
|
67
|
+
|
|
64
68
|
|
|
65
69
|
// Constants
|
|
66
70
|
const DEFAULT_MAX_REQUEST_MS = session_vars.get_setup_timeout_ms();
|
|
@@ -92,7 +96,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
92
96
|
|
|
93
97
|
// Defaults
|
|
94
98
|
let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
|
|
95
|
-
log.info({
|
|
99
|
+
log.info({ code: `ALK0166`, context, },
|
|
100
|
+
`Waiting for the docassemble server to respond. Will wait for ${ timeout/1000 } seconds at most.`
|
|
101
|
+
);
|
|
96
102
|
|
|
97
103
|
try {
|
|
98
104
|
let start_time = Date.now()
|
|
@@ -100,7 +106,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
100
106
|
let response = await _da_REST.get_dev_id( timeout );
|
|
101
107
|
let end_time = Date.now()
|
|
102
108
|
if ( response && response.data && response.data.id ) {
|
|
103
|
-
log.info({
|
|
109
|
+
log.info({ code: `ALK0167`, context, },
|
|
110
|
+
`The docassemble server responded after ${ (end_time - start_time)/1000 } seconds.`
|
|
111
|
+
);
|
|
104
112
|
return false;
|
|
105
113
|
}
|
|
106
114
|
|
|
@@ -109,17 +117,16 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
109
117
|
if ( is_timeout_error( error ) ) {
|
|
110
118
|
// Anything that's waiting for the server to not be busy does not want
|
|
111
119
|
// to move forward without a free server.
|
|
112
|
-
log.info({
|
|
120
|
+
log.info({ code: `ALK0168`, context, },
|
|
121
|
+
`The docassemble server was still busy after ${ timeout/1000 } seconds.`
|
|
122
|
+
);
|
|
113
123
|
} else {
|
|
114
124
|
// Not sure what to do for other errors. It could be a server
|
|
115
125
|
// error, like an 'Access Denied' error, which should be handled.
|
|
116
|
-
log.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
+ `ran into an unexpected error. The error didn't indicate a busy server, though. Error message:\n`
|
|
121
|
-
+ error
|
|
122
|
-
});
|
|
126
|
+
log.warning({ code: `ALK0169`, context, },
|
|
127
|
+
`Checked if the server was busy and ran into a different error`,
|
|
128
|
+
error
|
|
129
|
+
);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
throw error;
|
|
@@ -187,7 +194,7 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
187
194
|
* to allow for server request.
|
|
188
195
|
*/
|
|
189
196
|
|
|
190
|
-
let
|
|
197
|
+
let auth_feedback = {
|
|
191
198
|
codes: [ 1 ],
|
|
192
199
|
has_auth: null,
|
|
193
200
|
messages: [`API key authorization is not yet determined.`],
|
|
@@ -196,16 +203,16 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
196
203
|
|
|
197
204
|
// Skip this for tests run from ALKilnInThePlayground
|
|
198
205
|
if ( session_vars.get_origin() === 'playground' ) {
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
auth_feedback.codes.push( 2 );
|
|
207
|
+
auth_feedback.messages.push( `API keys are unnecessary for tests run in the Playground.` );
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
if ( typeof api_key !== `string` ) {
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
auth_feedback.codes.push( 3 );
|
|
212
|
+
auth_feedback.messages.push( `The given API key is not a string. Type: ${ typeof api_key }`);
|
|
206
213
|
}
|
|
207
214
|
|
|
208
|
-
if (
|
|
215
|
+
if ( auth_feedback.codes.length === 1 ) {
|
|
209
216
|
// Default timeouts
|
|
210
217
|
timeout = timeout || DEFAULT_MAX_REQUEST_MS;
|
|
211
218
|
try {
|
|
@@ -213,29 +220,31 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
213
220
|
api_key,
|
|
214
221
|
server_url: session_vars.get_da_server_url(),
|
|
215
222
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
auth_feedback.has_auth = true;
|
|
224
|
+
auth_feedback.codes = [ 6 ];
|
|
225
|
+
auth_feedback.messages = [ `This API key has at least basic authorization on this server.` ];
|
|
219
226
|
|
|
220
227
|
} catch ( error ) {
|
|
221
|
-
|
|
228
|
+
auth_feedback.error = error;
|
|
222
229
|
if ( error.status === 403 ) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
auth_feedback.has_auth = false;
|
|
231
|
+
auth_feedback.codes = [ 4 ];
|
|
232
|
+
auth_feedback.messages = [ `This API is unverified on this server. It is probably missing or has been deleted.` ];
|
|
226
233
|
} else {
|
|
227
234
|
// Note on 404, non-existent key: 404 == 403 - Since we're using the
|
|
228
235
|
// key itself to get its own info, if it doesn't exist, we'll get 403
|
|
229
|
-
|
|
230
|
-
|
|
236
|
+
auth_feedback.codes = [ 5 ];
|
|
237
|
+
auth_feedback.messages = [ `This API key threw a non-403 error.` ];
|
|
231
238
|
} // ends error status
|
|
232
239
|
} // ends try
|
|
233
|
-
} // ends
|
|
240
|
+
} // ends auth_feedback.codes only has the first code
|
|
234
241
|
|
|
235
|
-
log.debug({
|
|
236
|
-
|
|
242
|
+
log.debug({ code: `ALK0202`, context, },
|
|
243
|
+
`API key - what happened during authentication:`,
|
|
244
|
+
auth_feedback
|
|
245
|
+
);
|
|
237
246
|
|
|
238
|
-
return
|
|
247
|
+
return auth_feedback;
|
|
239
248
|
}; // Ends da_i.get_api_key_access_status()
|
|
240
249
|
|
|
241
250
|
// node -e 'require("./lib/docassemble/docassemble_api_interface.js").delete_project( "create1" )'
|
|
@@ -247,16 +256,19 @@ da_i.delete_project = async function ( delete_options ) {
|
|
|
247
256
|
let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
|
|
248
257
|
|
|
249
258
|
let project_name = session_vars.get_project_name()
|
|
250
|
-
log.info({
|
|
259
|
+
log.info({ code: `ALK0170`, context, },
|
|
260
|
+
`Trying to delete Project ${ project_name }`
|
|
261
|
+
);
|
|
251
262
|
|
|
252
263
|
try {
|
|
253
264
|
let response = await _da_REST.delete_project( timeout );
|
|
254
|
-
log.info({
|
|
265
|
+
log.info({ code: `ALK0171`, context, },
|
|
266
|
+
`Deleted Project "${ project_name }"`
|
|
267
|
+
);
|
|
255
268
|
} catch ( error ) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw error;
|
|
269
|
+
log.throw({ code: `ALK0172`, context, error: error },
|
|
270
|
+
`Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
|
|
271
|
+
);
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
}; // Ends da_i.delete_project()
|
|
@@ -280,7 +292,9 @@ da_i.quietly_try_to_delete_interviews = async function ({ interviews = {}, api_k
|
|
|
280
292
|
});
|
|
281
293
|
responses.push( sub_responses );
|
|
282
294
|
} else {
|
|
283
|
-
log.debug({
|
|
295
|
+
log.debug({ code: `ALK0191`, context, },
|
|
296
|
+
`There is no interview to delete.`
|
|
297
|
+
);
|
|
284
298
|
}
|
|
285
299
|
}
|
|
286
300
|
return responses;
|
|
@@ -295,9 +309,9 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
|
|
|
295
309
|
let responses = [];
|
|
296
310
|
for ( let api_key of api_keys ) {
|
|
297
311
|
try {
|
|
298
|
-
log.debug({
|
|
299
|
-
|
|
300
|
-
|
|
312
|
+
log.debug({ code: `ALK0188`, context, },
|
|
313
|
+
`Trying to delete the interview with the id ${ interview.uid } and an API key.`
|
|
314
|
+
);
|
|
301
315
|
|
|
302
316
|
// Actual work:
|
|
303
317
|
let response = await _da_REST.delete_interview( timeout, {
|
|
@@ -306,13 +320,15 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
|
|
|
306
320
|
});
|
|
307
321
|
responses.push( response );
|
|
308
322
|
|
|
309
|
-
log.debug({
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
log.debug({ code: `ALK0189`, context, },
|
|
324
|
+
`No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
|
|
325
|
+
);
|
|
312
326
|
} catch ( error ) {
|
|
313
327
|
error.interview = interview;
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
log.debug({ code: `ALK0190`, context, },
|
|
329
|
+
`Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`,
|
|
330
|
+
error
|
|
331
|
+
);
|
|
316
332
|
}
|
|
317
333
|
} // ends for api keys
|
|
318
334
|
return responses;
|
|
@@ -347,7 +363,9 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
|
|
|
347
363
|
if ( interview && interview.uid ) {
|
|
348
364
|
for ( let api_key of api_keys ) {
|
|
349
365
|
try {
|
|
350
|
-
log.debug({
|
|
366
|
+
log.debug({ code: `ALK0192`, context, },
|
|
367
|
+
`Trying to get the interview with the id ${ interview.uid } and an API key.`
|
|
368
|
+
);
|
|
351
369
|
|
|
352
370
|
// Actual work:
|
|
353
371
|
let response = await _da_REST.get_interview( timeout, {
|
|
@@ -356,20 +374,28 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
|
|
|
356
374
|
});
|
|
357
375
|
|
|
358
376
|
if ( response && response.data && response.data.items && response.data.items[0] && response.data.items[0].session ) {
|
|
359
|
-
log.debug({
|
|
377
|
+
log.debug({ code: `ALK0193`, context, },
|
|
378
|
+
`Status ${ response.status }. Got ${ response.data.items[0].session }.`
|
|
379
|
+
);
|
|
360
380
|
return response.data.items[0];
|
|
361
381
|
} else {
|
|
362
|
-
log.debug({
|
|
382
|
+
log.debug({ code: `ALK0194`, context, },
|
|
383
|
+
`Status ${ response.status }. Cannot find ${ interview.uid }`
|
|
384
|
+
);
|
|
363
385
|
}
|
|
364
386
|
|
|
365
387
|
} catch ( error ) {
|
|
366
388
|
error.interview = interview;
|
|
367
|
-
|
|
368
|
-
|
|
389
|
+
log.debug({ code: `ALK0195`, context, },
|
|
390
|
+
`${ error.status || `No error.status.` } ${ error.statusText || error.data || `No error.statusText.` } Ran into an error when getting the interview ${ interview.uid }. See https://docassemble.org/docs/api.html#interviews.`,
|
|
391
|
+
error
|
|
392
|
+
);
|
|
369
393
|
}
|
|
370
394
|
} // ends for api_keys
|
|
371
395
|
} else {
|
|
372
|
-
log.debug({
|
|
396
|
+
log.debug({ code: `ALK0196`, context, },
|
|
397
|
+
`There is no interview to get.`
|
|
398
|
+
);
|
|
373
399
|
}
|
|
374
400
|
|
|
375
401
|
return null;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const session_vars = require('../utils/session_vars');
|
|
4
|
-
const
|
|
4
|
+
const Log = require('../utils/Log');
|
|
5
5
|
const fg = require('fast-glob');
|
|
6
6
|
|
|
7
|
+
const context = `server_install`;
|
|
8
|
+
|
|
7
9
|
const server_install = async () => {
|
|
10
|
+
/** For packages we are installing on a temporary GitHub docassemble server.
|
|
11
|
+
*
|
|
12
|
+
* - Store that the package is being installed globally on a server as
|
|
13
|
+
* opposed to on a specific author's account (their Playground).
|
|
14
|
+
* - In the repo, find and store the path to directory that stores the
|
|
15
|
+
* package itself instead of the whole repo. For example, EvictionDefense
|
|
16
|
+
* in the docassemble-EvictionDefense repo.
|
|
17
|
+
* */
|
|
8
18
|
session_vars.save_install_method(`server`);
|
|
9
19
|
|
|
10
20
|
// Get path to folders inside repo main "docassemble" folder
|
|
@@ -23,20 +33,33 @@ const server_install = async () => {
|
|
|
23
33
|
// Get names of all folders in repo "docassemble" folder
|
|
24
34
|
let chosen_folder = folders[0];
|
|
25
35
|
if ( folders.length === 0 ) {
|
|
26
|
-
let msg = `There are 0 folders at ${path}. Stopping tests.`;
|
|
27
|
-
log.
|
|
28
|
-
|
|
36
|
+
let msg = `ALKiln cannot find the package's files. There are 0 folders at "${ path }". Stopping tests.`;
|
|
37
|
+
log.throw({ code: `ALK0044`, context,
|
|
38
|
+
error: new ReferenceError( msg ) // no logs
|
|
39
|
+
});
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
if ( folders.length > 1 ) {
|
|
32
|
-
log.warn({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
43
|
+
log.warn({ code: `ALK0045`, context, },
|
|
44
|
+
`There are ${ folders.length } folders at "${ path }": "${ folders.join('", "') }". ALKiln chose to use the folder "${ chosen_folder }".`
|
|
45
|
+
);
|
|
46
|
+
} else {
|
|
47
|
+
log.info({ code: `ALK0218`, context, },
|
|
48
|
+
`ALKiln found the package folder at "${ chosen_folder }".`
|
|
49
|
+
);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
session_vars.save_repo_folder_name( chosen_folder );
|
|
40
53
|
}; // Ends server_install();
|
|
41
54
|
|
|
55
|
+
// 1. session_vars - make sure all the required env vars exist
|
|
56
|
+
session_vars.validateEnvironment();
|
|
57
|
+
|
|
58
|
+
// 2. Prepare logs
|
|
59
|
+
// Local devs: This will be separate from your test paths
|
|
60
|
+
// unless you pass a `path` argument into the cucumber run command
|
|
61
|
+
const argv = require(`minimist`)(process.argv.slice(2));
|
|
62
|
+
const log = new Log({ path: argv.path, context, });
|
|
63
|
+
|
|
64
|
+
// 3. run
|
|
42
65
|
server_install();
|
package/lib/docassemble/setup.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// TODO: Rename to playground_install?
|
|
4
|
-
|
|
5
3
|
const child_process = require(`child_process`);
|
|
6
4
|
|
|
7
|
-
const
|
|
5
|
+
const Log = require(`../utils/Log`);
|
|
8
6
|
const session_vars = require(`../utils/session_vars` );
|
|
7
|
+
const files = require(`../utils/files` );
|
|
8
|
+
const time = require(`../utils/time` );
|
|
9
|
+
|
|
10
|
+
const context = `setup`;
|
|
9
11
|
|
|
10
12
|
const setup = async () => {
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
/** This is for a package being installed in an author's account (Playground)
|
|
14
|
+
* on their docassemble server. Using the author's
|
|
15
|
+
* DOCASSEMBLE_DEVELOPER_API_KEY secret, create a new Project, pull code
|
|
16
|
+
* into that project, and, if needed, wait for the server to reload.
|
|
17
|
+
*/
|
|
16
18
|
|
|
17
19
|
// Make and save project name
|
|
18
20
|
let project_name = session_vars.get_project_base_name().replace(/[^A-Za-z0-9]/g, '') + `${Date.now()}`;
|
|
19
21
|
session_vars.save_project_name( project_name );
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
const starting_msg = `Starting to upload this package to `
|
|
22
24
|
+ `a new Project called ${project_name} in the Playground `
|
|
23
|
-
+ `of your server's testing account. It may take a couple of minutes
|
|
25
|
+
+ `of your server's testing account. It may take a couple of minutes.`;
|
|
26
|
+
log.info({ code: `ALK0046`, context, },
|
|
27
|
+
starting_msg
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// TODO: 1. Start listening to the normal console here (for debug logging)
|
|
24
31
|
|
|
25
32
|
// dainstall . --apiurl https://site.org --apikey $API_KEY --playground --project=name
|
|
26
33
|
// I think this won't run on windows? Why do I think this? Where on the
|
|
@@ -34,29 +41,37 @@ const setup = async () => {
|
|
|
34
41
|
`--playground`, `--project=${ project_name }`
|
|
35
42
|
],
|
|
36
43
|
// Prints messages from the process to the console while it's running
|
|
44
|
+
// result.stderr, result.stdout, etc. will be `null`
|
|
37
45
|
{ stdio: 'inherit', }
|
|
38
46
|
);
|
|
39
47
|
|
|
40
|
-
//
|
|
41
|
-
// the response to find out the error code
|
|
48
|
+
// TODO: 2. Stop listening to the normal console here
|
|
42
49
|
|
|
50
|
+
// 403 is invalid API key, but I'm not sure how to output
|
|
51
|
+
// the response to find out the error code. stderr is `null`.
|
|
43
52
|
if ( result.status !== 0 ) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const install_err_msg = `Status code ${ result.status }. Could not `
|
|
54
|
+
+ `dainstall this package on your server. See above for more details.`;
|
|
55
|
+
log.throw({ code: `ALK0047`, context,
|
|
56
|
+
before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
|
|
57
|
+
error: `Error while installing project` },
|
|
58
|
+
install_err_msg
|
|
59
|
+
);
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
log.success({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
pre: `ALKiln created a Project in your Playground named "${ project_name }"!`
|
|
56
|
-
});
|
|
62
|
+
log.success({ code: `ALK0048`, context, },
|
|
63
|
+
`ALKiln created a Project in your Playground named "${ project_name }"!`
|
|
64
|
+
);
|
|
57
65
|
}; // Ends setup();
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
// Before everything, make sure all the required env vars exist
|
|
67
|
+
// 1. session_vars - make sure all the required env vars exist
|
|
61
68
|
session_vars.validateEnvironment();
|
|
69
|
+
|
|
70
|
+
// 2. Prepare logs
|
|
71
|
+
// Local devs: This will be separate from your test paths
|
|
72
|
+
// unless you pass a `path` argument into the cucumber run command
|
|
73
|
+
const argv = require(`minimist`)(process.argv.slice(2));
|
|
74
|
+
const log = new Log({ path: argv.path, context, });
|
|
75
|
+
|
|
76
|
+
// 3. run
|
|
62
77
|
setup();
|