forge-sql-orm 1.0.24 → 1.0.26

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.
@@ -1,8 +1,10 @@
1
1
  import { UpdateQueryResponse } from "@forge/sql";
2
- import type { EntityName, LoggingOptions } from "..";
2
+ import type { EntityName, LoggingOptions, QBFilterQuery } from "..";
3
3
  import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
4
4
  import type { QueryBuilder } from "@mikro-orm/knex/query";
5
5
  import type { Knex } from "knex";
6
+ import { EntityKey } from "@mikro-orm/core";
7
+ import {SqlParameters} from "@forge/sql/out/sql-statement";
6
8
 
7
9
  /**
8
10
  * Interface representing the main ForgeSQL operations.
@@ -27,6 +29,10 @@ export interface ForgeSqlOrmOptions {
27
29
  * Enables logging of raw SQL queries in the Atlassian Forge Developer Console.
28
30
  */
29
31
  logRawSqlQuery?: boolean;
32
+ /**
33
+ * Disable optimistic locking
34
+ */
35
+ disableOptimisticLocking?: boolean
30
36
  }
31
37
 
32
38
  /**
@@ -41,6 +47,14 @@ export interface SchemaSqlForgeSql {
41
47
  */
42
48
  executeSchemaSQL<T extends object>(query: string, schema: EntitySchema<T>): Promise<T[]>;
43
49
 
50
+ /**
51
+ * Executes a schema-bound SQL query and maps the only one result to the specified entity schema.
52
+ * @param query - The SQL query to execute.
53
+ * @param schema - The entity schema.
54
+ * @returns A list of mapped entity objects.
55
+ */
56
+ executeSchemaSQLOnlyOne<T extends object>(query: string, schema: EntitySchema<T>): Promise<T|undefined>;
57
+
44
58
  /**
45
59
  * Executes a raw SQL query and returns the results.
46
60
  * @param query - The raw SQL query.
@@ -51,9 +65,11 @@ export interface SchemaSqlForgeSql {
51
65
  /**
52
66
  * Executes a raw SQL update query.
53
67
  * @param query - The raw SQL update query.
68
+ * @param params - Sql parameters.
54
69
  * @returns The update response containing affected rows.
55
70
  */
56
- executeRawUpdateSQL(query: string): Promise<UpdateQueryResponse>;
71
+ executeRawUpdateSQL(query: string, params?: SqlParameters[]): Promise<UpdateQueryResponse>;
72
+
57
73
  }
58
74
 
59
75
  /**
@@ -83,10 +99,52 @@ export interface CRUDForgeSQL {
83
99
 
84
100
  /**
85
101
  * Updates a record by its ID.
102
+ * * If a version field is defined in the schema, versioning is applied:
103
+ * * the current record version is retrieved, checked for concurrent modifications,
104
+ * * and then incremented.
105
+ * *
106
+ * * @param entity - The entity with updated values.
107
+ * * @param schema - The entity schema.
108
+ * * @throws If the primary key is not included in the update fields.
109
+ * */
110
+ updateById<T extends object>(entity: Partial<T>, schema: EntitySchema<T>): Promise<void>;
111
+
112
+ /**
113
+ * Updates specified fields of records based on provided conditions.
114
+ * If the "where" parameter is not provided, the WHERE clause is built from the entity fields
115
+ * that are not included in the list of fields to update.
116
+ *
117
+ * @param entity - The object containing values to update and potential criteria for filtering.
118
+ * @param fields - Array of field names to update.
119
+ * @param schema - The entity schema.
120
+ * @param where - Optional filtering conditions for the WHERE clause.
121
+ * @returns The number of affected rows.
122
+ * @throws If no filtering criteria are provided (either via "where" or from the remaining entity fields).
123
+ */
124
+ updateFields<T extends object>(
125
+ entity: Partial<T>,
126
+ fields: EntityKey<T>[],
127
+ schema: EntitySchema<T>,
128
+ where?: QBFilterQuery<T>,
129
+ ): Promise<number>
130
+
131
+ /**
132
+ * Updates specific fields of a record identified by its primary key.
133
+ *
134
+ * If a version field is defined in the schema, versioning is applied:
135
+ * the current record version is retrieved, checked for concurrent modifications,
136
+ * and then incremented.
137
+ *
86
138
  * @param entity - The entity with updated values.
139
+ * @param fields - The list of field names to update.
87
140
  * @param schema - The entity schema.
141
+ * @throws If the primary key is not included in the update fields.
88
142
  */
89
- updateById<T extends object>(entity: T, schema: EntitySchema<T>): Promise<void>;
143
+ updateFieldById<T extends object>(
144
+ entity: T,
145
+ fields: EntityKey<T>[],
146
+ schema: EntitySchema<T>,
147
+ ): Promise<void>;
90
148
  }
91
149
 
92
150
  /**
@@ -2,6 +2,7 @@ import { sql, UpdateQueryResponse } from "@forge/sql";
2
2
  import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
3
3
  import { parseDateTime } from "../utils/sqlUtils";
4
4
  import { ForgeSqlOrmOptions, SchemaSqlForgeSql } from "./ForgeSQLQueryBuilder";
5
+ import {SqlParameters} from "@forge/sql/out/sql-statement";
5
6
 
6
7
  export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
7
8
  private readonly options: ForgeSqlOrmOptions;
@@ -10,6 +11,17 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
10
11
  this.options = options;
11
12
  }
12
13
 
14
+ async executeSchemaSQLOnlyOne<T extends object>(query: string, schema: EntitySchema<T>): Promise<T|undefined> {
15
+ const results = await this.executeSchemaSQL(query, schema);
16
+ if (!results || results.length === 0){
17
+ return undefined;
18
+ }
19
+ if (results.length>1){
20
+ throw new Error('Expected 1 record but returned '+results.length)
21
+ }
22
+ return results[0];
23
+ }
24
+
13
25
  /**
14
26
  * Executes a schema-based SQL query and maps the result to the entity schema.
15
27
  * @param query - The SQL query to execute.
@@ -33,10 +45,10 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
33
45
 
34
46
  switch (p.type) {
35
47
  case "datetime":
36
- newModel[fieldName] = parseDateTime(
37
- rawModel[rawFieldName] as string,
38
- "YYYY-MM-DDTHH:mm:ss.SSS",
39
- );
48
+ newModel[fieldName] = parseDateTime(
49
+ rawModel[rawFieldName] as string,
50
+ "YYYY-MM-DDTHH:mm:ss.SSS",
51
+ );
40
52
  break;
41
53
  case "date":
42
54
  newModel[fieldName] = parseDateTime(rawModel[rawFieldName] as string, "YYYY-MM-DD");
@@ -68,13 +80,14 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
68
80
  /**
69
81
  * Executes a raw SQL update query.
70
82
  * @param query - The raw SQL update query.
83
+ * @param params - sql parameters.
71
84
  * @returns The update response containing affected rows.
72
85
  */
73
- async executeRawUpdateSQL(query: string): Promise<UpdateQueryResponse> {
74
- if (this.options.logRawSqlQuery) {
75
- console.debug("Executing update SQL: " + query);
76
- }
86
+ async executeRawUpdateSQL(query: string, params?: SqlParameters[]): Promise<UpdateQueryResponse> {
77
87
  const sqlStatement = sql.prepare<UpdateQueryResponse>(query);
88
+ if (params) {
89
+ sqlStatement.bindParams(params);
90
+ }
78
91
  const updateQueryResponseResults = await sqlStatement.execute();
79
92
  return updateQueryResponseResults.rows;
80
93
  }
@@ -2,24 +2,33 @@ import { types } from "@mikro-orm/core/types";
2
2
  import moment from "moment";
3
3
  import { AnyString } from "@mikro-orm/core/typings";
4
4
 
5
- export const transformValue = (value: {
5
+ const wrapIfNeeded=(data:string, wrap:boolean):string => {
6
+ return wrap?`'${data}'`:data;
7
+ }
8
+
9
+ export const transformValue = <U>(value: {
6
10
  type: keyof typeof types | AnyString;
7
- value: unknown;
8
- }): unknown => {
11
+ value: U;
12
+ }, wrapValue: boolean = false): U => {
9
13
  switch (value.type) {
10
14
  case "text":
11
15
  case "string":
12
- return `'${value.value}'`;
16
+ return <U>wrapIfNeeded(`${value.value}`,wrapValue);
13
17
  case "datetime":
14
- return `'${moment(value.value as Date).format("YYYY-MM-DDTHH:mm:ss.SSS")}'`;
18
+ return <U>wrapIfNeeded(`${moment(value.value as Date).format("YYYY-MM-DDTHH:mm:ss.SSS")}`,wrapValue);
15
19
  case "date":
16
- return `'${moment(value.value as Date).format("YYYY-MM-DD")}'`;
20
+ return <U>wrapIfNeeded(`${moment(value.value as Date).format("YYYY-MM-DD")}`, wrapValue);
17
21
  case "time":
18
- return `'${moment(value.value as Date).format("HH:mm:ss.SSS")}'`;
22
+ return <U>wrapIfNeeded(`${moment(value.value as Date).format("HH:mm:ss.SSS")}`,wrapValue);
19
23
  default:
20
24
  return value.value;
21
25
  }
22
26
  };
27
+
23
28
  export const parseDateTime = (value: string, format: string): Date => {
24
- return moment(value, format).toDate();
29
+ const m = moment(value, format, true);
30
+ if (!m.isValid()) {
31
+ return moment(value).toDate();
32
+ }
33
+ return m.toDate();
25
34
  };