Skip to content

Commit

Permalink
fix: match processing + improved logging
Browse files Browse the repository at this point in the history
  • Loading branch information
ahochsteger committed Aug 23, 2023
1 parent 1fd5eab commit 16c2ffb
Show file tree
Hide file tree
Showing 16 changed files with 251 additions and 58 deletions.
26 changes: 17 additions & 9 deletions docs/config-schema-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,11 @@ Represents a config to match a certain GMail attachment
|**contentType**|`string`|A RegEx matching the content type of the attachment|No, default: `".*"`|
|**includeAttachments**|`boolean`|Should regular attachments be included in attachment processing (default: true)|No, default: `true`|
|**includeInlineImages**|`boolean`|Should inline images be included in attachment processing (default: true)|No, default: `true`|
|**largerThan**|`number`|Only include attachments larger than the given size in bytes|No, default: `-1`|
|**largerThan**|`number`|Only include attachments larger than the given size in bytes.
Set to `-1` to ignore it.|No, default: `-1`|
|**name**|`string`|A RegEx matching the name of the attachment|No, default: `"(.*)"`|
|**smallerThan**|`number`|Only include attachments smaller than the given size in bytes|No|
|**smallerThan**|`number`|Only include attachments smaller than the given size in bytes.
Set to `-1` to ignore it.|No, default: `-1`|

Additional properties are not allowed.

Expand All @@ -177,7 +179,8 @@ Should inline images be included in attachment processing (default: true)

### AttachmentMatchConfig.largerThan

Only include attachments larger than the given size in bytes
Only include attachments larger than the given size in bytes.
Set to `-1` to ignore it.

* **Type**: `number`
* **Required**: No, default: `-1`
Expand All @@ -191,10 +194,11 @@ A RegEx matching the name of the attachment

### AttachmentMatchConfig.smallerThan

Only include attachments smaller than the given size in bytes
Only include attachments smaller than the given size in bytes.
Set to `-1` to ignore it.

* **Type**: `number`
* **Required**: No
* **Required**: No, default: `-1`



Expand Down Expand Up @@ -760,8 +764,10 @@ Represents a config to match a certain GMail thread
|---|---|---|---|
|**firstMessageSubject**|`string`|The regex to match `firstMessageSubject`|No, default: `".*"`|
|**labels**|`string`|The regex to match at least one label|No, default: `".*"`|
|**maxMessageCount**|`number`|The maximum number of messages a matching thread is allowed to have|No, default: `-1`|
|**minMessageCount**|`number`|The minimum number of messages a matching thread must have|No, default: `1`|
|**maxMessageCount**|`number`|The maximum number of messages a matching thread is allowed to have.
Set to `-1` to ignore it.|No, default: `-1`|
|**minMessageCount**|`number`|The minimum number of messages a matching thread must have.
Set to `-1` to ignore it.|No, default: `1`|
|**query**|`string`|The GMail search query additional to the global query to find threads to be processed.
See [Search operators you can use with Gmail](https://support.google.com/mail/answer/7190?hl=en) for more information.|No, default: |

Expand All @@ -783,14 +789,16 @@ The regex to match at least one label

### ThreadMatchConfig.maxMessageCount

The maximum number of messages a matching thread is allowed to have
The maximum number of messages a matching thread is allowed to have.
Set to `-1` to ignore it.

* **Type**: `number`
* **Required**: No, default: `-1`

### ThreadMatchConfig.minMessageCount

The minimum number of messages a matching thread must have
The minimum number of messages a matching thread must have.
Set to `-1` to ignore it.

* **Type**: `number`
* **Required**: No, default: `1`
Expand Down
2 changes: 1 addition & 1 deletion docs/reference-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The following table lists all enum types and their values referenced in the conf
| <a id="enum.MetaInfoType">`MetaInfoType`</a> | The type of meta information used for context substitution placerholders. | <ul><li><a id="enum.MetaInfoType.boolean">`boolean`</a>: Boolean type substituted to `true` or `false`.</li><li><a id="enum.MetaInfoType.date">`date`</a>: Date/time type. For substitution a format string can be given using `${<placeholder>:format:<formatstring>}`.</li><li><a id="enum.MetaInfoType.number">`number`</a>: A numeric data type.</li><li><a id="enum.MetaInfoType.string">`string`</a>: A string data type.</li><li><a id="enum.MetaInfoType.variable">`variable`</a>: A custom configuration variable.</li></ul> |
| <a id="enum.PlaceholderType">`PlaceholderType`</a> | The type of a placeholder. | <ul><li><a id="enum.PlaceholderType.attachment">`attachment`</a>: An attachment placeholder type.</li><li><a id="enum.PlaceholderType.message">`message`</a>: A message placeholder type.</li><li><a id="enum.PlaceholderType.thread">`thread`</a>: A thread placeholder type.</li></ul> |
| <a id="enum.ProcessingStage">`ProcessingStage`</a> | The stage of processing | <ul><li><a id="enum.ProcessingStage.main">`main`</a>: The stage during processing the main object (thread, message, attachment)</li><li><a id="enum.ProcessingStage.post-main">`post-main`</a>: The stage after processing the main object (thread, message, attachment)</li><li><a id="enum.ProcessingStage.pre-main">`pre-main`</a>: The stage before processing the main object (thread, message, attachment)</li></ul> |
| <a id="enum.ProcessingStatus">`ProcessingStatus`</a> | The result status of processing a config or an action. | <ul><li><a id="enum.ProcessingStatus.error">`error`</a>: An error has occured.</li><li><a id="enum.ProcessingStatus.ok">`ok`</a>: The processing was successful.</li></ul> |
| <a id="enum.ProcessingStatus">`ProcessingStatus`</a> | The result status of processing a config or an action. | <ul><li><a id="enum.ProcessingStatus.error">`error`</a>: An error has occurred.</li><li><a id="enum.ProcessingStatus.ok">`ok`</a>: The processing was successful.</li></ul> |
| <a id="enum.RunMode">`RunMode`</a> | The runtime mode in which processing takes place. | <ul><li><a id="enum.RunMode.dangerous">`dangerous`</a>: This run-mode will execute all configured actions including possibly destructive actions like overwriting files or removing threads or messages.<br>ATTENTION: Use this only if you know exactly what you're doing and won't complain if something goes wrong!</li><li><a id="enum.RunMode.dry-run">`dry-run`</a>: This run-mode skips execution of writing actions. Use this for testing config changes or library upgrades.</li><li><a id="enum.RunMode.safe-mode">`safe-mode`</a>: This run-mode can be used for normal uperation but will skip possibly destructive actions like overwriting files or removing threads or messages.</li></ul> |

## Substitution Placeholder
Expand Down
3 changes: 3 additions & 0 deletions src/lib/Context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ it("should create a new processing result", () => {
const expected: ProcessingResult = {
status: ProcessingStatus.OK,
performedActions: [],
processedAttachments: 0,
processedMessages: 0,
processedThreads: 0,
}
expect(actual).toMatchObject(expected)
})
Expand Down
8 changes: 7 additions & 1 deletion src/lib/Context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export type AttachmentContext = MessageContext & {

/** The result status of processing a config or an action. */
export enum ProcessingStatus {
/** An error has occured. */
/** An error has occurred. */
ERROR = "error",
/** The processing was successful. */
OK = "ok",
Expand All @@ -165,12 +165,18 @@ export class ProcessingError extends Error {
export type ProcessingResult = {
status: ProcessingStatus
performedActions: ActionConfig[]
processedThreads: number
processedMessages: number
processedAttachments: number
failedAction?: ActionConfig
error?: Error
}
export function newProcessingResult(): ProcessingResult {
return {
status: ProcessingStatus.OK,
performedActions: [],
processedAttachments: 0,
processedMessages: 0,
processedThreads: 0,
}
}
2 changes: 1 addition & 1 deletion src/lib/config/AttachmentConfig.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ it("should ensure nested object defaults", () => {
expect(attachmentConfig.match.includeAttachments).toBe(true)
expect(attachmentConfig.match.includeInlineImages).toBe(true)
expect(attachmentConfig.match.largerThan).toBe(-1)
expect(attachmentConfig.match.smallerThan).toBe(Number.MAX_SAFE_INTEGER)
expect(attachmentConfig.match.smallerThan).toBe(-1)
})
8 changes: 5 additions & 3 deletions src/lib/config/AttachmentMatchConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ export class AttachmentMatchConfig {
*/
includeInlineImages? = true
/**
* Only include attachments larger than the given size in bytes
* Only include attachments larger than the given size in bytes.
* Set to `-1` to ignore it.
*/
largerThan? = -1
/**
* A RegEx matching the name of the attachment
*/
name? = "(.*)"
/**
* Only include attachments smaller than the given size in bytes
* Only include attachments smaller than the given size in bytes.
* Set to `-1` to ignore it.
*/
smallerThan? = Number.MAX_SAFE_INTEGER
smallerThan? = -1
}

export type RequiredAttachmentMatchConfig = RequiredDeep<AttachmentMatchConfig>
Expand Down
6 changes: 4 additions & 2 deletions src/lib/config/ThreadMatchConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ export class ThreadMatchConfig {
labels? = ".*"

/**
* The maximum number of messages a matching thread is allowed to have
* The maximum number of messages a matching thread is allowed to have.
* Set to `-1` to ignore it.
*/
@Expose()
maxMessageCount? = -1

/**
* The minimum number of messages a matching thread must have
* The minimum number of messages a matching thread must have.
* Set to `-1` to ignore it.
*/
@Expose()
minMessageCount? = 1
Expand Down
9 changes: 5 additions & 4 deletions src/lib/config/config-schema-v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
},
"largerThan": {
"default": -1,
"description": "Only include attachments larger than the given size in bytes",
"description": "Only include attachments larger than the given size in bytes.\nSet to `-1` to ignore it.",
"title": "largerThan",
"type": "number"
},
Expand All @@ -134,7 +134,8 @@
"type": "string"
},
"smallerThan": {
"description": "Only include attachments smaller than the given size in bytes",
"default": -1,
"description": "Only include attachments smaller than the given size in bytes.\nSet to `-1` to ignore it.",
"title": "smallerThan",
"type": "number"
}
Expand Down Expand Up @@ -577,13 +578,13 @@
},
"maxMessageCount": {
"default": -1,
"description": "The maximum number of messages a matching thread is allowed to have",
"description": "The maximum number of messages a matching thread is allowed to have.\nSet to `-1` to ignore it.",
"title": "maxMessageCount",
"type": "number"
},
"minMessageCount": {
"default": 1,
"description": "The minimum number of messages a matching thread must have",
"description": "The minimum number of messages a matching thread must have.\nSet to `-1` to ignore it.",
"title": "minMessageCount",
"type": "number"
},
Expand Down
10 changes: 8 additions & 2 deletions src/lib/processors/AttachmentProcessor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ it("should build a match config with special globals", () => {
expect(actual).toMatchObject(expected)
})

it("should match messages with matching parameters", () => {
it("should match attachments with matching parameters", () => {
const matchExamples: { config: AttachmentMatchConfig; matched: string[] }[] =
[
{
Expand Down Expand Up @@ -123,7 +123,13 @@ it("should match messages with matching parameters", () => {
})
const res = []
for (const att of mockedMessage.getAttachments()) {
if (AttachmentProcessor.matches(attachmentConfig.match, att)) {
if (
AttachmentProcessor.matches(
mocks.messageContext,
attachmentConfig.match,
att,
)
) {
res.push(att.getName())
}
}
Expand Down
57 changes: 47 additions & 10 deletions src/lib/processors/AttachmentProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { ProcessingStage } from "../config/ActionConfig"
import { RequiredAttachmentConfig } from "../config/AttachmentConfig"
import {
AttachmentMatchConfig,
newAttachmentMatchConfig,
RequiredAttachmentMatchConfig,
} from "../config/AttachmentMatchConfig"
import {
Expand Down Expand Up @@ -42,14 +41,42 @@ export class AttachmentProcessor extends BaseProcessor {
return attachmentContext
}
public static matches(
ctx: MessageContext,
matchConfig: RequiredAttachmentMatchConfig,
attachment: GoogleAppsScript.Gmail.GmailAttachment,
) {
if (!attachment.getContentType().match(matchConfig.contentType))
return false
if (!attachment.getName().match(matchConfig.name)) return false
if (attachment.getSize() <= matchConfig.largerThan) return false
if (attachment.getSize() >= matchConfig.smallerThan) return false
return this.noMatch(
ctx,
`contentType '${attachment.getContentType()}' does not match '${
matchConfig.contentType
}'`,
)
if (!attachment.getName().match(matchConfig.name))
return this.noMatch(
ctx,
`name '${attachment.getName()}' does not match '${matchConfig.name}'`,
)
if (
matchConfig.largerThan != -1 &&
attachment.getSize() <= matchConfig.largerThan
)
return this.noMatch(
ctx,
`size ${attachment.getSize()} not larger than ${
matchConfig.largerThan
}`,
)
if (
matchConfig.smallerThan != -1 &&
attachment.getSize() >= matchConfig.smallerThan
)
return this.noMatch(
ctx,
`size ${attachment.getSize()} not smaller than ${
matchConfig.smallerThan
}`,
)
return true
}

Expand All @@ -58,23 +85,32 @@ export class AttachmentProcessor extends BaseProcessor {
global: RequiredAttachmentMatchConfig,
local: RequiredAttachmentMatchConfig,
): RequiredAttachmentMatchConfig {
return newAttachmentMatchConfig({
const matchConfig: RequiredAttachmentMatchConfig = {
contentType: PatternUtil.substitute(
ctx,
`${global.contentType}|${local.contentType}`.replace(".*|", ""),
).replace("|.*", ""),
includeAttachments: global.includeAttachments && local.includeAttachments,
includeInlineImages:
global.includeInlineImages && local.includeInlineImages,
largerThan: Math.max(global.largerThan, local.largerThan),
largerThan: this.effectiveMaxNumber(
global.largerThan,
local.largerThan,
-1,
),
name: PatternUtil.substitute(
ctx,
`${global.name}|${local.name}`
.replace("(.*)|", "")
.replace("|(.*)", ""),
),
smallerThan: Math.min(global.smallerThan, local.smallerThan),
})
smallerThan: this.effectiveMinNumber(
global.smallerThan,
local.smallerThan,
-1,
),
}
return matchConfig
}

public static getRegexMapFromAttachmentMatchConfig(
Expand Down Expand Up @@ -178,7 +214,7 @@ export class AttachmentProcessor extends BaseProcessor {
const attachments = ctx.message.object.getAttachments(opts)
for (let index = 0; index < attachments.length; index++) {
const attachment = attachments[index]
if (!this.matches(matchConfig, attachment)) {
if (!this.matches(ctx, matchConfig, attachment)) {
ctx.log.debug(
`Skipping non-matching attachment hash '${attachment.getHash()}' (name:'${attachment.getName()}', type:${attachment.getContentType()}, size:${attachment.getSize()}) started ...`,
)
Expand Down Expand Up @@ -231,6 +267,7 @@ export class AttachmentProcessor extends BaseProcessor {
ctx.attachment.config.actions,
ctx.proc.config.global.attachment.actions,
)
result.processedAttachments += 1
ctx.log.info(
`Processing of attachment hash '${attachment.getHash()}' finished.`,
)
Expand Down
40 changes: 40 additions & 0 deletions src/lib/processors/BaseProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export abstract class BaseProcessor {
return true
}

protected static noMatch(ctx: ProcessingContext, message: string): boolean {
ctx.log.debug(`-> Reason: ${message}`)
return false
}

protected static isSet(
value: boolean | number | string | undefined,
unsetValue?: unknown,
Expand All @@ -137,6 +142,19 @@ export abstract class BaseProcessor {
return this.isSet(value) ? value : defaultVal
}

protected static effectiveCSV(
global: string | undefined,
local: string,
): string {
return [
...new Set(
[...(global ?? "").split(","), ...(local ?? "").split(",")].map((v) =>
v.trim(),
),
),
].join(",")
}

protected static effectiveValue<T extends boolean | string | number>(
global: T | undefined,
local: T,
Expand All @@ -149,6 +167,28 @@ export abstract class BaseProcessor {
: unsetValue
}

protected static effectiveMaxNumber(
global: number | undefined,
local: number,
unsetValue: number,
): number {
global = global ?? unsetValue
if (global === unsetValue) return local
if (local === unsetValue) return global
return Math.max(global, local)
}

protected static effectiveMinNumber(
global: number | undefined,
local: number,
unsetValue: number,
): number {
global = global ?? unsetValue
if (global === unsetValue) return local
if (local === unsetValue) return global
return Math.min(global, local)
}

protected static effectiveNumber(
global: number | undefined,
local: number,
Expand Down
Loading

0 comments on commit 16c2ffb

Please sign in to comment.