@things-factory/shell 10.1.146 → 10.1.149
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/dist-server/introspection-for-signed-in.d.ts +14 -0
- package/dist-server/introspection-for-signed-in.js +43 -0
- package/dist-server/introspection-for-signed-in.js.map +1 -0
- package/dist-server/server-dev.js +3 -1
- package/dist-server/server-dev.js.map +1 -1
- package/dist-server/server.js +4 -7
- package/dist-server/server.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/typeorm/money-column.d.ts +12 -0
- package/dist-server/typeorm/money-column.js +21 -0
- package/dist-server/typeorm/money-column.js.map +1 -1
- package/package.json +19 -3
- package/tests/introspection-for-signed-in.test.ts +48 -0
- package/tests/money-column.test.ts +25 -1
|
@@ -32,3 +32,15 @@ export declare const decimalTransformer: ValueTransformer;
|
|
|
32
32
|
* `nullable`·`default` 같은 나머지 옵션은 호출하는 쪽이 정한다. GraphQL 필드는 `Float` 로 둔다.
|
|
33
33
|
*/
|
|
34
34
|
export declare function moneyColumn(options?: ColumnOptions): ColumnOptions;
|
|
35
|
+
/**
|
|
36
|
+
* A quantity — `decimal(18, 6)`: a count, a weight, a length, a dose.
|
|
37
|
+
*
|
|
38
|
+
* `float` and `double precision` do not hold 0.1 exactly, and `'double precision'` is a Postgres name that the other
|
|
39
|
+
* drivers do not take. Six places carry grams in kilograms and millilitres in litres; twelve before the point carry
|
|
40
|
+
* any stock a site keeps. Read back as a number, as `moneyColumn` is. The GraphQL field stays `Float`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function quantityColumn(options?: ColumnOptions): ColumnOptions;
|
|
43
|
+
/** A rate — `decimal(18, 6)`: won per unit of a currency, a unit price per unit of measure. */
|
|
44
|
+
export declare function rateColumn(options?: ColumnOptions): ColumnOptions;
|
|
45
|
+
/** A percentage such as an interest rate — `decimal(9, 4)`. */
|
|
46
|
+
export declare function percentColumn(options?: ColumnOptions): ColumnOptions;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.decimalTransformer = void 0;
|
|
4
4
|
exports.moneyColumn = moneyColumn;
|
|
5
|
+
exports.quantityColumn = quantityColumn;
|
|
6
|
+
exports.rateColumn = rateColumn;
|
|
7
|
+
exports.percentColumn = percentColumn;
|
|
5
8
|
/**
|
|
6
9
|
* `decimal` 컬럼을 숫자로 읽고 쓰는 변환기.
|
|
7
10
|
*
|
|
@@ -40,4 +43,22 @@ exports.decimalTransformer = {
|
|
|
40
43
|
function moneyColumn(options = {}) {
|
|
41
44
|
return { type: 'decimal', precision: 18, scale: 2, transformer: exports.decimalTransformer, ...options };
|
|
42
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* A quantity — `decimal(18, 6)`: a count, a weight, a length, a dose.
|
|
48
|
+
*
|
|
49
|
+
* `float` and `double precision` do not hold 0.1 exactly, and `'double precision'` is a Postgres name that the other
|
|
50
|
+
* drivers do not take. Six places carry grams in kilograms and millilitres in litres; twelve before the point carry
|
|
51
|
+
* any stock a site keeps. Read back as a number, as `moneyColumn` is. The GraphQL field stays `Float`.
|
|
52
|
+
*/
|
|
53
|
+
function quantityColumn(options = {}) {
|
|
54
|
+
return { type: 'decimal', precision: 18, scale: 6, transformer: exports.decimalTransformer, ...options };
|
|
55
|
+
}
|
|
56
|
+
/** A rate — `decimal(18, 6)`: won per unit of a currency, a unit price per unit of measure. */
|
|
57
|
+
function rateColumn(options = {}) {
|
|
58
|
+
return { type: 'decimal', precision: 18, scale: 6, transformer: exports.decimalTransformer, ...options };
|
|
59
|
+
}
|
|
60
|
+
/** A percentage such as an interest rate — `decimal(9, 4)`. */
|
|
61
|
+
function percentColumn(options = {}) {
|
|
62
|
+
return { type: 'decimal', precision: 9, scale: 4, transformer: exports.decimalTransformer, ...options };
|
|
63
|
+
}
|
|
43
64
|
//# sourceMappingURL=money-column.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"money-column.js","sourceRoot":"","sources":["../../server/typeorm/money-column.ts"],"names":[],"mappings":";;;AAsCA,kCAEC;
|
|
1
|
+
{"version":3,"file":"money-column.js","sourceRoot":"","sources":["../../server/typeorm/money-column.ts"],"names":[],"mappings":";;;AAsCA,kCAEC;AASD,wCAEC;AAGD,gCAEC;AAGD,sCAEC;AA3DD;;;;;;GAMG;AACU,QAAA,kBAAkB,GAAqB;IAClD,EAAE,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACjH,IAAI,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;CAC1F,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAgB,WAAW,CAAC,UAAyB,EAAE;IACrD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,0BAAkB,EAAE,GAAG,OAAO,EAAE,CAAA;AAClG,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,UAAyB,EAAE;IACxD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,0BAAkB,EAAE,GAAG,OAAO,EAAE,CAAA;AAClG,CAAC;AAED,+FAA+F;AAC/F,SAAgB,UAAU,CAAC,UAAyB,EAAE;IACpD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,0BAAkB,EAAE,GAAG,OAAO,EAAE,CAAA;AAClG,CAAC;AAED,+DAA+D;AAC/D,SAAgB,aAAa,CAAC,UAAyB,EAAE;IACvD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,0BAAkB,EAAE,GAAG,OAAO,EAAE,CAAA;AACjG,CAAC","sourcesContent":["import { ColumnOptions, ValueTransformer } from 'typeorm'\n\n/**\n * `decimal` 컬럼을 숫자로 읽고 쓰는 변환기.\n *\n * Postgres·MySQL 은 `decimal` 을 **문자열**로 돌려준다(정밀도를 지키려고). 그대로 두면\n * GraphQL `Float` 필드와 합산 코드가 `'1200.00' + 300` 같은 글자 이어붙이기를 한다.\n * 그래서 읽을 때 숫자로 바꾼다. 비어 있음(`null`·`undefined`)은 그대로 둔다 — 0 으로 만들지 않는다.\n */\nexport const decimalTransformer: ValueTransformer = {\n to: (value: unknown) => (value === undefined ? undefined : value === null || value === '' ? null : Number(value)),\n from: (value: unknown) => (value === null || value === undefined ? value : Number(value))\n}\n\n/**\n * 금액 컬럼 — `decimal(18, 2)`.\n *\n * ## 왜 `decimal` 인가\n *\n * - 형식을 적지 않은 `number` 컬럼은 32비트 정수가 된다. 원 단위로는 정확하지만 한 칸에\n * 약 21억까지이고 소수(센트)를 못 담는다.\n * - `float` 은 0.1 을 정확히 담지 못한다. 수천 줄을 더하면 은행 잔액과 어긋난다.\n * - `decimal` 은 SQL 표준이라 다섯 드라이버(postgres·mysql/mariadb·sqlite·mssql·oracle)가 모두 받는다.\n * 18자리 중 소수 2자리 — 원화 금액 전부와 외화 센트를 담는다.\n *\n * sqlite 는 자릿수를 강제하지 않는다(NUMERIC 친화형). 정수 원화는 정확하지만 소수 금액은\n * 내부에서 실수로 담길 수 있다 — 소수 금액의 정확성은 서버 DB 를 전제로 한다.\n *\n * ## 쓰는 법\n *\n * ```ts\n * @Column({ nullable: true, ...moneyColumn() })\n * @Field(type => Float, { nullable: true })\n * supplyAmount?: number\n * ```\n *\n * `nullable`·`default` 같은 나머지 옵션은 호출하는 쪽이 정한다. GraphQL 필드는 `Float` 로 둔다.\n */\nexport function moneyColumn(options: ColumnOptions = {}): ColumnOptions {\n return { type: 'decimal', precision: 18, scale: 2, transformer: decimalTransformer, ...options }\n}\n\n/**\n * A quantity — `decimal(18, 6)`: a count, a weight, a length, a dose.\n *\n * `float` and `double precision` do not hold 0.1 exactly, and `'double precision'` is a Postgres name that the other\n * drivers do not take. Six places carry grams in kilograms and millilitres in litres; twelve before the point carry\n * any stock a site keeps. Read back as a number, as `moneyColumn` is. The GraphQL field stays `Float`.\n */\nexport function quantityColumn(options: ColumnOptions = {}): ColumnOptions {\n return { type: 'decimal', precision: 18, scale: 6, transformer: decimalTransformer, ...options }\n}\n\n/** A rate — `decimal(18, 6)`: won per unit of a currency, a unit price per unit of measure. */\nexport function rateColumn(options: ColumnOptions = {}): ColumnOptions {\n return { type: 'decimal', precision: 18, scale: 6, transformer: decimalTransformer, ...options }\n}\n\n/** A percentage such as an interest rate — `decimal(9, 4)`. */\nexport function percentColumn(options: ColumnOptions = {}): ColumnOptions {\n return { type: 'decimal', precision: 9, scale: 4, transformer: decimalTransformer, ...options }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/shell",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.149",
|
|
4
4
|
"description": "Core module for framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"things-factory": "bin/things-factory",
|
|
@@ -12,6 +12,22 @@
|
|
|
12
12
|
},
|
|
13
13
|
"main": "dist-server/index.js",
|
|
14
14
|
"browser": "client/index.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"browser": "./client/index.ts",
|
|
18
|
+
"default": "./dist-server/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./refusal": "./dist-server/refusal/refusal.js",
|
|
21
|
+
"./client": "./client/index.ts",
|
|
22
|
+
"./*": "./*"
|
|
23
|
+
},
|
|
24
|
+
"typesVersions": {
|
|
25
|
+
"*": {
|
|
26
|
+
"refusal": [
|
|
27
|
+
"dist-server/refusal/refusal.d.ts"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
15
31
|
"author": "Hearty Oh <heartyoh@hatiolab.com>",
|
|
16
32
|
"things-factory": true,
|
|
17
33
|
"license": "MIT",
|
|
@@ -54,7 +70,7 @@
|
|
|
54
70
|
"@operato/utils": "^10.0.0",
|
|
55
71
|
"@reduxjs/toolkit": "^2.2.5",
|
|
56
72
|
"@things-factory/ejs-remote": "^10.1.20",
|
|
57
|
-
"@things-factory/env": "^10.1.
|
|
73
|
+
"@things-factory/env": "^10.1.149",
|
|
58
74
|
"@things-factory/operato-license-checker": "^4.0.4",
|
|
59
75
|
"@things-factory/styles": "^10.1.123",
|
|
60
76
|
"@things-factory/utils": "^10.1.20",
|
|
@@ -126,5 +142,5 @@
|
|
|
126
142
|
"pg": "^8.7.3",
|
|
127
143
|
"sqlite3": "^5.0.8"
|
|
128
144
|
},
|
|
129
|
-
"gitHead": "
|
|
145
|
+
"gitHead": "816f88a9fa6110935a3f71740458db8f7469d2e3"
|
|
130
146
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ApolloServer } from '@apollo/server'
|
|
2
|
+
import { buildSchema } from 'graphql'
|
|
3
|
+
|
|
4
|
+
import { introspectionForSignedIn } from '../server/introspection-for-signed-in'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Schema reading is open to a signed-in user and refused to anyone else; `__typename` is always open
|
|
8
|
+
* (ADR-0126). The server keeps Apollo's introspection on, as shell's servers do.
|
|
9
|
+
*/
|
|
10
|
+
const schema = buildSchema(`type Query { hello: String }`)
|
|
11
|
+
|
|
12
|
+
const server = new ApolloServer({ schema, introspection: true, plugins: [introspectionForSignedIn()] })
|
|
13
|
+
|
|
14
|
+
beforeAll(() => server.start())
|
|
15
|
+
afterAll(() => server.stop())
|
|
16
|
+
|
|
17
|
+
const ask = async (query: string, user?: object) => {
|
|
18
|
+
const response = await server.executeOperation({ query }, { contextValue: { state: { user } } })
|
|
19
|
+
return response.body.kind === 'single' ? response.body.singleResult : undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe('reading the schema', () => {
|
|
23
|
+
it('is refused without a signed-in user', async () => {
|
|
24
|
+
const result = await ask('{ __schema { queryType { name } } }')
|
|
25
|
+
expect(result?.errors?.[0]?.extensions?.code).toBe('UNAUTHENTICATED')
|
|
26
|
+
expect(result?.data).toBeUndefined()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('is refused for __type and inside a fragment too', async () => {
|
|
30
|
+
expect((await ask('{ __type(name: "Query") { name } }'))?.errors?.[0]?.extensions?.code).toBe('UNAUTHENTICATED')
|
|
31
|
+
expect(
|
|
32
|
+
(await ask('query { ...schema } fragment schema on Query { __schema { queryType { name } } }'))?.errors?.[0]
|
|
33
|
+
?.extensions?.code
|
|
34
|
+
).toBe('UNAUTHENTICATED')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('answers a signed-in user', async () => {
|
|
38
|
+
const result = await ask('{ __schema { queryType { name } } }', { id: 'u-1' })
|
|
39
|
+
expect(result?.errors).toBeUndefined()
|
|
40
|
+
expect((result?.data as any).__schema.queryType.name).toBe('Query')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('leaves __typename open to everyone', async () => {
|
|
44
|
+
const result = await ask('{ __typename }')
|
|
45
|
+
expect(result?.errors).toBeUndefined()
|
|
46
|
+
expect((result?.data as any).__typename).toBe('Query')
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decimalTransformer, moneyColumn } from '../server/typeorm/money-column'
|
|
1
|
+
import { decimalTransformer, moneyColumn, percentColumn, quantityColumn, rateColumn } from '../server/typeorm/money-column'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Money is a `decimal(18, 2)` column read back as a number.
|
|
@@ -32,3 +32,27 @@ describe('a money column', () => {
|
|
|
32
32
|
expect(decimalTransformer.to('658630')).toBe(658630)
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A quantity, a rate and a percentage are decimals as well. `float` and `'double precision'` lose 0.1 and the second
|
|
38
|
+
* is a Postgres name the other drivers refuse.
|
|
39
|
+
*/
|
|
40
|
+
describe('the other decimal columns', () => {
|
|
41
|
+
it('a quantity is decimal(18, 6) read back as a number', () => {
|
|
42
|
+
expect(quantityColumn()).toEqual({ type: 'decimal', precision: 18, scale: 6, transformer: decimalTransformer })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('a rate is decimal(18, 6) read back as a number', () => {
|
|
46
|
+
expect(rateColumn()).toEqual({ type: 'decimal', precision: 18, scale: 6, transformer: decimalTransformer })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('a percentage is decimal(9, 4) read back as a number', () => {
|
|
50
|
+
expect(percentColumn()).toEqual({ type: 'decimal', precision: 9, scale: 4, transformer: decimalTransformer })
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('each keeps the options the caller gives', () => {
|
|
54
|
+
for (const column of [quantityColumn, rateColumn, percentColumn]) {
|
|
55
|
+
expect(column({ nullable: true, default: 0 })).toMatchObject({ type: 'decimal', nullable: true, default: 0 })
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
})
|