Skip to content

Commit 3999897

Browse files
committed
MLE-2554 TS for more documents functions
Functions: removeAll, patch, protect, wipe, advanceLsqt, and improved write as well. Added "temporal-admin" to the rest-writer test user. Which should allow for the currently-skipped temporal tests to be enabled soon.
1 parent 387f954 commit 3999897

File tree

5 files changed

+368
-3
lines changed

5 files changed

+368
-3
lines changed

.github/copilot-instructions.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
When generating TypeScript definitions, follow this advice:
2+
3+
All types will go into marklogic.d.ts.
4+
5+
Determining the output type is difficult. You have to look at the module containing the
6+
implementation code and look for an "outputTransform". The implementation of that function
7+
should reveal what the user-facing function will return.
8+
9+
Please add "runtime" tests to the test-typescript directory. These tests should do
10+
"smoke" tests that - critically - verify the output of each function.

marklogic.d.ts

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,60 @@ declare module 'marklogic' {
128128
systemTime?: string;
129129
}
130130

131+
/**
132+
* Result from a removeAll operation.
133+
*/
134+
export interface RemoveAllResult {
135+
/** Always false (indicates removal operation completed) */
136+
exists: boolean;
137+
/** Collection that was removed (if specified) */
138+
collection?: string;
139+
/** Directory that was removed (if specified) */
140+
directory?: string;
141+
/** Whether all documents were removed */
142+
allDocuments?: boolean;
143+
}
144+
145+
/**
146+
* Result from a protect operation on a temporal document.
147+
*/
148+
export interface ProtectResult {
149+
/** The URI of the protected document */
150+
uri: string;
151+
/** The temporal collection name */
152+
temporalCollection: string;
153+
/** The protection level (noWipe, noDelete, or noUpdate) */
154+
level: string;
155+
}
156+
157+
/**
158+
* Result from a wipe operation on a temporal document.
159+
*/
160+
export interface WipeResult {
161+
/** The URI of the wiped document */
162+
uri: string;
163+
/** The temporal collection name */
164+
temporalCollection: string;
165+
/** Whether the document was wiped */
166+
wiped: boolean;
167+
}
168+
169+
/**
170+
* Result from advancing LSQT on a temporal collection.
171+
*/
172+
export interface AdvanceLsqtResult {
173+
/** The new Last Stable Query Time */
174+
lsqt: string;
175+
}
176+
177+
/**
178+
* Result from a patch operation.
179+
*/
180+
export interface PatchResult {
181+
/** The URI of the patched document */
182+
uri: string;
183+
}
184+
131185
/**
132186
* Result from a write operation.
133187
*/
@@ -163,12 +217,129 @@ declare module 'marklogic' {
163217
*/
164218
write(documents: DocumentDescriptor | DocumentDescriptor[]): ResultProvider<WriteResult>;
165219

220+
/**
221+
* Writes one or more documents with additional parameters.
222+
* @param params - Configuration object with documents and optional parameters
223+
* @returns A result provider that resolves to a write result with document URIs
224+
*/
225+
write(params: {
226+
/** The document(s) to write */
227+
documents: DocumentDescriptor | DocumentDescriptor[];
228+
/** Categories of information to write */
229+
categories?: string | string[];
230+
/** Transaction id or Transaction object */
231+
txid?: string | object;
232+
/** Transform to apply on the server */
233+
transform?: string | object;
234+
/** Forest name to write to */
235+
forestName?: string;
236+
/** Temporal collection for temporal documents */
237+
temporalCollection?: string;
238+
/** System time for temporal documents (ISO 8601 string or Date object) */
239+
systemTime?: string | Date;
240+
}): ResultProvider<WriteResult>;
241+
166242
/**
167243
* Removes one or more documents.
168244
* @param uris - A URI string or array of URI strings
169245
* @returns A result provider that resolves to a remove result
170246
*/
171247
remove(uris: string | string[]): ResultProvider<RemoveResult>;
248+
249+
/**
250+
* Removes all documents in a collection, directory, or database.
251+
* Requires rest-admin role to delete all documents, rest-writer role otherwise.
252+
* @param params - Configuration object with collection, directory, all, or txid properties
253+
* @returns A result provider that resolves to a remove all result
254+
*/
255+
removeAll(params: {
256+
/** The collection whose documents should be deleted */
257+
collection?: string;
258+
/** A directory whose documents should be deleted */
259+
directory?: string;
260+
/** Delete all documents (requires rest-admin role) */
261+
all?: boolean;
262+
/** Transaction id or Transaction object */
263+
txid?: string | object;
264+
}): ResultProvider<RemoveAllResult>;
265+
266+
/**
267+
* Protects a temporal document from certain operations.
268+
* Must specify either duration or expireTime.
269+
* @param params - Configuration object with either duration or expireTime
270+
* @returns A result provider that resolves to protect result
271+
*/
272+
protect(params: {
273+
/** The URI of the temporal document */
274+
uri: string;
275+
/** The temporal collection name */
276+
temporalCollection: string;
277+
/** Protection level: 'noWipe' | 'noDelete' | 'noUpdate' (default: 'noDelete') */
278+
level?: string;
279+
/** Archive path for the document */
280+
archivePath?: string;
281+
} & (
282+
{ /** Duration as XSD duration string (e.g., 'P30D') */ duration: string; expireTime?: never; } |
283+
{ /** Expire time (alternative to duration) */ expireTime: string; duration?: never; }
284+
)): ResultProvider<ProtectResult>;
285+
286+
/**
287+
* Deletes all versions of a temporal document.
288+
* @param params - Configuration object with uri and temporalCollection
289+
* @returns A result provider that resolves to wipe result
290+
*/
291+
wipe(params: {
292+
/** The URI of the temporal document to wipe */
293+
uri: string;
294+
/** The temporal collection name */
295+
temporalCollection: string;
296+
}): ResultProvider<WipeResult>;
297+
298+
/**
299+
* Advances the LSQT (Last Stable Query Time) of a temporal collection.
300+
* @param params - Configuration object or temporal collection name
301+
* @returns A result provider that resolves to the new LSQT
302+
*/
303+
advanceLsqt(params: string | {
304+
/** The temporal collection name */
305+
temporalCollection: string;
306+
/** Lag in seconds to subtract from maximum system start time */
307+
lag?: number;
308+
}): ResultProvider<AdvanceLsqtResult>;
309+
310+
/**
311+
* Applies changes to a document using patch operations.
312+
* @param params - Configuration object with uri and operations
313+
* @returns A result provider that resolves to patch result
314+
*/
315+
patch(params: {
316+
/** The URI of the document to patch */
317+
uri: string;
318+
/** Patch operations (from patchBuilder) or raw patch string/Buffer */
319+
operations: any[] | string | Buffer;
320+
/** Categories of information to modify (typically 'content') */
321+
categories?: string | string[];
322+
/** Temporal collection name (for temporal documents) */
323+
temporalCollection?: string;
324+
/** Temporal document URI */
325+
temporalDocument?: string;
326+
/** Source document URI */
327+
sourceDocument?: string;
328+
/** Transaction id or Transaction object */
329+
txid?: string | object;
330+
/** Version identifier for optimistic locking */
331+
versionId?: string;
332+
/** Format: 'json' or 'xml' */
333+
format?: string;
334+
}): ResultProvider<PatchResult>;
335+
336+
/**
337+
* Applies changes to a document using patch operations.
338+
* @param uri - The URI of the document to patch
339+
* @param operations - One or more patch operations from patchBuilder
340+
* @returns A result provider that resolves to patch result
341+
*/
342+
patch(uri: string, ...operations: any[]): ResultProvider<PatchResult>;
172343
}
173344

174345
/**

test-app/src/main/ml-config/security/users/rest-writer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"password": "x",
55
"role": [
66
"rest-writer",
7-
"rest-evaluator"
7+
"rest-evaluator",
8+
"temporal-admin"
89
]
9-
}
10+
}

test-typescript/documents-runtime.test.ts

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* Run with: npm run test:compile && npx mocha test-typescript/*.js
1616
*/
1717

18-
import should = require('should');
18+
const should = require('should');
1919
import type { DatabaseClient } from 'marklogic';
2020

2121
const testConfig = require('../etc/test-config.js');
@@ -111,4 +111,152 @@ describe('Documents API runtime validation', function() {
111111
const probeResult = await client.documents.probe(testUri).result();
112112
probeResult.exists.should.equal(false);
113113
});
114+
115+
it('removeAll() returns ResultProvider with RemoveAllResult', async function() {
116+
const testCollection = 'typescript-test-collection';
117+
const testUri1 = '/test-typescript/removeAll-test-1.json';
118+
const testUri2 = '/test-typescript/removeAll-test-2.json';
119+
120+
// Write documents to a collection
121+
await client.documents.write([
122+
{
123+
uri: testUri1,
124+
content: { test: 'doc1' },
125+
contentType: 'application/json',
126+
collections: [testCollection]
127+
},
128+
{
129+
uri: testUri2,
130+
content: { test: 'doc2' },
131+
contentType: 'application/json',
132+
collections: [testCollection]
133+
}
134+
]).result();
135+
136+
// Remove all documents in the collection
137+
const resultProvider = client.documents.removeAll({
138+
collection: testCollection
139+
});
140+
141+
// Verify ResultProvider has result() method
142+
resultProvider.should.have.property('result');
143+
resultProvider.result.should.be.a.Function();
144+
145+
const result = await resultProvider.result();
146+
147+
// Verify RemoveAllResult structure
148+
result.should.have.property('exists', false);
149+
result.should.have.property('collection', testCollection);
150+
151+
// Verify documents were actually removed
152+
const probe1 = await client.documents.probe(testUri1).result();
153+
const probe2 = await client.documents.probe(testUri2).result();
154+
probe1.exists.should.equal(false);
155+
probe2.exists.should.equal(false);
156+
});
157+
158+
it('patch() returns ResultProvider with PatchResult', async function() {
159+
const testUri = '/test-typescript/patch-test.json';
160+
161+
// Write a document first
162+
await client.documents.write({
163+
uri: testUri,
164+
content: { name: 'Original', count: 1 },
165+
contentType: 'application/json'
166+
}).result();
167+
168+
// Patch the document using patchBuilder
169+
const p = marklogic.patchBuilder;
170+
const resultProvider = client.documents.patch(
171+
testUri,
172+
p.replace('/name', 'Updated'),
173+
p.replace('/count', 2)
174+
);
175+
176+
// Verify ResultProvider has result() method
177+
resultProvider.should.have.property('result');
178+
resultProvider.result.should.be.a.Function();
179+
180+
const result = await resultProvider.result();
181+
182+
// Verify PatchResult structure
183+
result.should.have.property('uri', testUri);
184+
185+
// Verify document was actually patched
186+
const docs = await client.documents.read(testUri).result();
187+
docs[0].content.should.have.property('name', 'Updated');
188+
docs[0].content.should.have.property('count', 2);
189+
190+
// Clean up
191+
await client.documents.remove(testUri).result();
192+
});
193+
194+
it('protect() returns ResultProvider with ProtectResult', async function() {
195+
// Note: Requires temporal document to exist and may need temporal-admin role
196+
this.skip();
197+
198+
const testUri = '/test-typescript/temporal-doc.json';
199+
const temporalCollection = 'temporalCollection';
200+
201+
const resultProvider = client.documents.protect({
202+
uri: testUri,
203+
temporalCollection: temporalCollection,
204+
duration: 'P30D',
205+
level: 'noDelete'
206+
});
207+
208+
resultProvider.should.have.property('result');
209+
resultProvider.result.should.be.a.Function();
210+
211+
const result = await resultProvider.result();
212+
213+
// Verify ProtectResult structure
214+
result.should.have.property('uri', testUri);
215+
result.should.have.property('temporalCollection', temporalCollection);
216+
result.should.have.property('level', 'noDelete');
217+
});
218+
219+
it('wipe() returns ResultProvider with WipeResult', async function() {
220+
// Note: Requires admin privileges and temporal document to exist
221+
this.skip();
222+
223+
const testUri = '/test-typescript/temporal-wipe-doc.json';
224+
const temporalCollection = 'temporalCollection';
225+
226+
const resultProvider = client.documents.wipe({
227+
uri: testUri,
228+
temporalCollection: temporalCollection
229+
});
230+
231+
resultProvider.should.have.property('result');
232+
resultProvider.result.should.be.a.Function();
233+
234+
const result = await resultProvider.result();
235+
236+
// Verify WipeResult structure
237+
result.should.have.property('uri', testUri);
238+
result.should.have.property('temporalCollection', temporalCollection);
239+
result.should.have.property('wiped', true);
240+
});
241+
242+
it('advanceLsqt() returns ResultProvider with AdvanceLsqtResult', async function() {
243+
// Note: Requires temporal-admin or admin role
244+
this.skip();
245+
246+
const temporalCollection = 'temporalCollection';
247+
248+
const resultProvider = client.documents.advanceLsqt({
249+
temporalCollection: temporalCollection,
250+
lag: 10
251+
});
252+
253+
resultProvider.should.have.property('result');
254+
resultProvider.result.should.be.a.Function();
255+
256+
const result = await resultProvider.result();
257+
258+
// Verify AdvanceLsqtResult structure
259+
result.should.have.property('lsqt');
260+
result.lsqt.should.be.a.String();
261+
});
114262
});

0 commit comments

Comments
 (0)