@things-factory/ops-master 10.1.33 → 10.1.34

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.
Files changed (66) hide show
  1. package/dist-client/index.d.ts +1 -0
  2. package/dist-client/index.js +1 -0
  3. package/dist-client/index.js.map +1 -1
  4. package/dist-client/master-change-approval.d.ts +7 -0
  5. package/dist-client/master-change-approval.js +20 -0
  6. package/dist-client/master-change-approval.js.map +1 -0
  7. package/dist-client/pages/master/test-specification.d.ts +22 -85
  8. package/dist-client/pages/master/test-specification.js +190 -405
  9. package/dist-client/pages/master/test-specification.js.map +1 -1
  10. package/dist-client/tsconfig.tsbuildinfo +1 -1
  11. package/dist-server/service/asset/asset-master-resolver.js +38 -34
  12. package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
  13. package/dist-server/service/asset/index.d.ts +2 -2
  14. package/dist-server/service/equipment/equipment-master-resolver.js +54 -49
  15. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  16. package/dist-server/service/equipment/index.d.ts +2 -2
  17. package/dist-server/service/index.d.ts +8 -2
  18. package/dist-server/service/index.js +8 -0
  19. package/dist-server/service/index.js.map +1 -1
  20. package/dist-server/service/location/index.d.ts +1 -1
  21. package/dist-server/service/location/location-master-resolver.js +34 -31
  22. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  23. package/dist-server/service/master-change-policy.d.ts +23 -0
  24. package/dist-server/service/master-change-policy.js +129 -0
  25. package/dist-server/service/master-change-policy.js.map +1 -0
  26. package/dist-server/service/master-crud.js +6 -0
  27. package/dist-server/service/master-crud.js.map +1 -1
  28. package/dist-server/service/material/index.d.ts +2 -2
  29. package/dist-server/service/material/material-master-resolver.js +55 -51
  30. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  31. package/dist-server/service/operation/index.d.ts +1 -1
  32. package/dist-server/service/operation/operation-master-resolver.js +28 -25
  33. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  34. package/dist-server/service/personnel/index.d.ts +2 -2
  35. package/dist-server/service/personnel/personnel-master-resolver.js +38 -34
  36. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
  37. package/dist-server/service/quality/nonconformance-cause-resolver.js +17 -14
  38. package/dist-server/service/quality/nonconformance-cause-resolver.js.map +1 -1
  39. package/dist-server/service/recipe/index.d.ts +2 -2
  40. package/dist-server/service/recipe/recipe-master-resolver.js +32 -28
  41. package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -1
  42. package/dist-server/service/test-spec/index.d.ts +3 -2
  43. package/dist-server/service/test-spec/index.js +4 -2
  44. package/dist-server/service/test-spec/index.js.map +1 -1
  45. package/dist-server/service/test-spec/test-spec-effective-list.d.ts +32 -0
  46. package/dist-server/service/test-spec/test-spec-effective-list.js +40 -0
  47. package/dist-server/service/test-spec/test-spec-effective-list.js.map +1 -0
  48. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +4 -14
  49. package/dist-server/service/test-spec/test-spec-master-resolver.js +12 -83
  50. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
  51. package/dist-server/service/test-spec/test-spec-query.js +7 -1
  52. package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
  53. package/dist-server/service/test-spec/test-spec-revision-resolver.d.ts +52 -0
  54. package/dist-server/service/test-spec/test-spec-revision-resolver.js +76 -0
  55. package/dist-server/service/test-spec/test-spec-revision-resolver.js.map +1 -0
  56. package/dist-server/service/test-spec/test-spec-revision-service.d.ts +117 -0
  57. package/dist-server/service/test-spec/test-spec-revision-service.js +519 -0
  58. package/dist-server/service/test-spec/test-spec-revision-service.js.map +1 -0
  59. package/dist-server/service/test-spec/test-spec-revision.d.ts +82 -0
  60. package/dist-server/service/test-spec/test-spec-revision.js +329 -0
  61. package/dist-server/service/test-spec/test-spec-revision.js.map +1 -0
  62. package/dist-server/service/test-spec/test-specification.d.ts +4 -0
  63. package/dist-server/service/test-spec/test-specification.js +11 -2
  64. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  65. package/dist-server/tsconfig.tsbuildinfo +1 -1
  66. package/package.json +9 -6
@@ -1,7 +1,8 @@
1
1
  import { ListParam } from '@things-factory/shell';
2
- import { MasterPatch } from '../master-crud';
2
+ import type { MasterPatch } from '../master-crud';
3
3
  import { CriterionPatch } from './criteria-rows';
4
4
  import { TestSpecification } from './test-specification';
5
+ /** Reads project the effective revision. Changes go through the revision draft resolver. */
5
6
  /**
6
7
  * 기준 한 줄 — **화면이 받는 모양.**
7
8
  *
@@ -29,13 +30,7 @@ export declare class TestSpecificationList {
29
30
  }
30
31
  export declare class TestSpecificationResolver {
31
32
  testSpecifications(params: ListParam, context: ResolverContext): Promise<TestSpecificationList>;
32
- /**
33
- * 그리드가 보낸 변경을 한 번에 적용한다.
34
- *
35
- * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을
36
- * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
37
- * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
38
- */
33
+ /** Explicitly closed old write door: even non-regulated publication must retain a revision. */
39
34
  updateMultipleTestSpecification(patches: MasterPatch[], context: ResolverContext): Promise<TestSpecification[]>;
40
35
  /**
41
36
  * 명세 하나의 기준을 **행으로** 낸다.
@@ -44,11 +39,6 @@ export declare class TestSpecificationResolver {
44
39
  * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.
45
40
  */
46
41
  testSpecificationCriteria(name: string, context: ResolverContext): Promise<TestSpecificationCriterionRow[]>;
47
- /**
48
- * 기준을 **바뀐 줄만** 받아 얹는다.
49
- *
50
- * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의
51
- * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.
52
- */
42
+ /** Criteria belong to a draft, not to the stable identity row. */
53
43
  updateTestSpecificationCriteria(name: string, patches: CriterionPatch[], context: ResolverContext): Promise<TestSpecificationCriterionRow[]>;
54
44
  }
@@ -4,54 +4,11 @@ exports.TestSpecificationResolver = exports.TestSpecificationList = exports.Test
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
7
- const master_crud_1 = require("../master-crud");
8
- const master_axis_1 = require("../master-axis");
9
- const master_list_1 = require("../master-list");
10
- const criteria_check_1 = require("./criteria-check");
11
7
  const criteria_rows_1 = require("./criteria-rows");
12
8
  const test_specification_1 = require("./test-specification");
13
- /**
14
- * 검사 명세 축을 채우고 고친다.
15
- *
16
- * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────
17
- * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의
18
- * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —
19
- * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.
20
- *
21
- * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.
22
- *
23
- * ── 기준의 내용까지는 공통 규칙이 못 본다 ──────────────────────────────────
24
- * 모양을 옮기는 일은 스키마가 한다 — `criteria` 는 `ScalarObject` 라 구조가 구조로 오간다.
25
- * 그러나 **판정에 쓸 수 있는 내용인가**는 이 축만 안다(단위가 있나, 하한이 상한보다 크지 않나).
26
- * 그래서 저장 전 검증만 여기서 든다.
27
- */
28
- /**
29
- * 패치 하나의 기준 칸을 저장할 모양으로 바꾼다.
30
- *
31
- * ── 왜 저장 전에 막나 ──────────────────────────────────────────────────────
32
- * 판정하는 쪽은 **모르는 것을 통과로 읽지 않도록** 만들어져 있다. 그래서 망가진 기준이 들어와도 터지지
33
- * 않고 조용히 「판정하지 않음」이나 「늘 한계 안」이 된다. 기준을 적은 사람은 적었다고 믿고 검사원은
34
- * 통과했다고 읽는다. 그 어긋남을 막을 수 있는 자리는 여기뿐이다.
35
- *
36
- * 규칙은 `criteria-check.ts` 가 든다. 여기는 칸을 꺼내 맡기고 답을 그대로 올린다.
37
- */
38
- function prepareCriteria(patch) {
39
- if (!('criteria' in patch)) {
40
- /* 기준을 안 건드린 패치다. 이름만 고치는 것도 정상이다. */
41
- return patch;
42
- }
43
- const read = (0, criteria_check_1.readCriteria)(patch.criteria);
44
- if (read.read === 'failed') {
45
- throw new Error(`검사 명세 "${patch.name ?? patch.id ?? ''}" — ${read.detail}`);
46
- }
47
- const verdict = (0, criteria_check_1.judgeCriteria)(read.criteria);
48
- if ((0, criteria_check_1.criteriaRejected)(verdict)) {
49
- /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */
50
- throw new Error(`검사 명세 "${patch.name ?? patch.id ?? ''}" 의 기준을 저장하지 않았습니다.\n` +
51
- verdict.problems.map(one => `· ${one}`).join('\n'));
52
- }
53
- return { ...patch, criteria: read.criteria };
54
- }
9
+ const test_spec_revision_service_1 = require("./test-spec-revision-service");
10
+ const test_spec_effective_list_1 = require("./test-spec-effective-list");
11
+ /** Reads project the effective revision. Changes go through the revision draft resolver. */
55
12
  /**
56
13
  * 기준 한 줄 — **화면이 받는 모양.**
57
14
  *
@@ -113,24 +70,11 @@ exports.TestSpecificationList = TestSpecificationList = tslib_1.__decorate([
113
70
  let TestSpecificationResolver = class TestSpecificationResolver {
114
71
  async testSpecifications(params, context) {
115
72
  const { domain } = context.state;
116
- const { items, total } = await (0, master_list_1.listMaster)('testSpecification', params, domain);
117
- return { items: items, total };
73
+ return (0, test_spec_effective_list_1.listEffectiveTestSpecifications)((0, shell_1.getRepository)(test_specification_1.TestSpecification).manager, domain, params);
118
74
  }
119
- /**
120
- * 그리드가 보낸 변경을 한 번에 적용한다.
121
- *
122
- * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을
123
- * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는
124
- * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.
125
- */
75
+ /** Explicitly closed old write door: even non-regulated publication must retain a revision. */
126
76
  async updateMultipleTestSpecification(patches, context) {
127
- const { domain, user, tx } = context.state;
128
- const axis = (0, master_axis_1.masterAxisOf)('testSpecification');
129
- const prepared = patches.map(patch => prepareCriteria(patch));
130
- const saved = await (0, master_crud_1.saveMasterPatches)(tx, domain, axis.entity, prepared, user, {
131
- hasEffectivePeriod: axis.hasEffectivePeriod
132
- });
133
- return saved;
77
+ throw new test_spec_revision_service_1.TestSpecificationRefusal('REVISION_REQUIRED', 'Use a test specification draft; direct identity-row changes are not supported.');
134
78
  }
135
79
  /**
136
80
  * 명세 하나의 기준을 **행으로** 낸다.
@@ -145,28 +89,12 @@ let TestSpecificationResolver = class TestSpecificationResolver {
145
89
  if (!spec) {
146
90
  throw new Error(`검사 명세 "${name}" 를 찾지 못했습니다`);
147
91
  }
148
- return (0, criteria_rows_1.toCriterionRows)(spec.criteria ?? []);
92
+ const effective = await (0, test_spec_revision_service_1.effectiveTestSpecification)((0, shell_1.getRepository)(test_specification_1.TestSpecification).manager, domain.id, spec);
93
+ return (0, criteria_rows_1.toCriterionRows)(effective.criteria ?? []);
149
94
  }
150
- /**
151
- * 기준을 **바뀐 줄만** 받아 얹는다.
152
- *
153
- * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의
154
- * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.
155
- */
95
+ /** Criteria belong to a draft, not to the stable identity row. */
156
96
  async updateTestSpecificationCriteria(name, patches, context) {
157
- const { domain, user, tx } = context.state;
158
- const repository = tx.getRepository(test_specification_1.TestSpecification);
159
- const spec = await repository.findOne({ where: { domain: { id: domain.id }, name } });
160
- if (!spec) {
161
- throw new Error(`검사 명세 "${name}" 를 찾지 못했습니다`);
162
- }
163
- const applied = (0, criteria_rows_1.applyCriterionPatches)(spec.criteria ?? [], patches);
164
- if (applied.applied === 'rejected') {
165
- /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */
166
- throw new Error(`검사 명세 "${name}" 의 기준을 저장하지 않았습니다.\n` + applied.problems.map(one => `· ${one}`).join('\n'));
167
- }
168
- await repository.save({ ...spec, criteria: applied.criteria, updater: user });
169
- return (0, criteria_rows_1.toCriterionRows)(applied.criteria);
97
+ throw new test_spec_revision_service_1.TestSpecificationRefusal('REVISION_REQUIRED', 'Edit the draft criteria, then submit the frozen revision for approval.');
170
98
  }
171
99
  };
172
100
  exports.TestSpecificationResolver = TestSpecificationResolver;
@@ -210,6 +138,7 @@ tslib_1.__decorate([
210
138
  tslib_1.__metadata("design:returntype", Promise)
211
139
  ], TestSpecificationResolver.prototype, "updateTestSpecificationCriteria", null);
212
140
  exports.TestSpecificationResolver = TestSpecificationResolver = tslib_1.__decorate([
213
- (0, type_graphql_1.Resolver)(test_specification_1.TestSpecification)
141
+ (0, type_graphql_1.Resolver)(test_specification_1.TestSpecification),
142
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
214
143
  ], TestSpecificationResolver);
215
144
  //# sourceMappingURL=test-spec-master-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-spec-master-resolver.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, Float, Int, Mutation, ObjectType, Field, Query, Resolver } from 'type-graphql'\n\nimport { ListParam, ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { MasterPatch, saveMasterPatches } from '../master-crud'\nimport { masterAxisOf } from '../master-axis'\nimport { listMaster } from '../master-list'\nimport { criteriaRejected, judgeCriteria, readCriteria } from './criteria-check'\nimport { CriterionPatch, CriterionRow, applyCriterionPatches, toCriterionRows } from './criteria-rows'\nimport { TestSpecification } from './test-specification'\n\n/**\n * 검사 명세 축을 채우고 고친다.\n *\n * ── 왜 마스터는 그리드에서 고칠 수 있나 ────────────────────────────────────\n * 업무 기록과 달리 마스터는 **선언**이다. 「이 사람이 용접 자격을 갖는다」는 일어난 일이 아니라 지금의\n * 규정이고, 규정은 바뀐다. 없앨 때 행을 지우지 않고 유효 기간의 끝을 채우는 것이 그래서다 —\n * 지우면 그것을 가리키는 지난 기록이 가리킬 것을 잃는다.\n *\n * 규칙은 `master-crud.ts` 한 곳에 있다. 여기는 축의 이름만 붙인다.\n *\n * ── 기준의 내용까지는 공통 규칙이 못 본다 ──────────────────────────────────\n * 모양을 옮기는 일은 스키마가 한다 — `criteria` 는 `ScalarObject` 라 구조가 구조로 오간다.\n * 그러나 **판정에 쓸 수 있는 내용인가**는 이 축만 안다(단위가 있나, 하한이 상한보다 크지 않나).\n * 그래서 저장 전 검증만 여기서 든다.\n */\n\n/**\n * 패치 하나의 기준 칸을 저장할 모양으로 바꾼다.\n *\n * ── 왜 저장 전에 막나 ──────────────────────────────────────────────────────\n * 판정하는 쪽은 **모르는 것을 통과로 읽지 않도록** 만들어져 있다. 그래서 망가진 기준이 들어와도 터지지\n * 않고 조용히 「판정하지 않음」이나 「늘 한계 안」이 된다. 기준을 적은 사람은 적었다고 믿고 검사원은\n * 통과했다고 읽는다. 그 어긋남을 막을 수 있는 자리는 여기뿐이다.\n *\n * 규칙은 `criteria-check.ts` 가 든다. 여기는 칸을 꺼내 맡기고 답을 그대로 올린다.\n */\nfunction prepareCriteria(patch: MasterPatch): MasterPatch {\n if (!('criteria' in patch)) {\n /* 기준을 안 건드린 패치다. 이름만 고치는 것도 정상이다. */\n return patch\n }\n\n const read = readCriteria(patch.criteria)\n\n if (read.read === 'failed') {\n throw new Error(`검사 명세 \"${patch.name ?? patch.id ?? ''}\" — ${read.detail}`)\n }\n\n const verdict = judgeCriteria(read.criteria)\n\n if (criteriaRejected(verdict)) {\n /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */\n throw new Error(\n `검사 명세 \"${patch.name ?? patch.id ?? ''}\" 의 기준을 저장하지 않았습니다.\\n` +\n verdict.problems.map(one => `· ${one}`).join('\\n')\n )\n }\n\n return { ...patch, criteria: read.criteria }\n}\n\n/**\n * 기준 한 줄 — **화면이 받는 모양.**\n *\n * 한계를 펴서 낸다. 화면은 이름 · 설명 · 하한 · 상한 · 단위 · 표현식이 각자 칸인 표를 그리고,\n * JSON 을 아예 모른다. 옮김은 `criteria-rows.ts` 한 곳이 든다 — 저장이 표로 내려가도 그 파일만 바뀐다.\n */\n@ObjectType({ description: 'One test specification criterion, flattened for editing' })\nexport class TestSpecificationCriterionRow {\n @Field({ description: 'criterion id — measurements find their criterion by this' })\n id: string\n\n @Field({ nullable: true, description: 'what is being checked' })\n description?: string\n\n @Field({ nullable: true, description: 'ISA-95 EvaluatedPropertyID — which property is measured' })\n evaluatedPropertyId?: string\n\n @Field(type => Float, { nullable: true, description: 'lower limit' })\n minimum?: number\n\n @Field(type => Float, { nullable: true, description: 'upper limit' })\n maximum?: number\n\n @Field({ nullable: true, description: 'unit of measure — required whenever a limit is given' })\n uom?: string\n\n @Field({ nullable: true, description: \"the site's own notation — never parsed\" })\n expression?: string\n\n /**\n * 화면이 아직 안 그리는 표준 칸들.\n *\n * **버리지 않는다.** 편 것을 다시 담을 때 이 칸들이 사라지면 사람이 적은 것이 조용히 없어진다.\n */\n @Field(type => ScalarObject, { nullable: true, description: 'standard fields this screen does not draw yet' })\n keptFields?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A page of test specifications' })\nexport class TestSpecificationList {\n @Field(type => [TestSpecification])\n items: TestSpecification[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(TestSpecification)\nexport class TestSpecificationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => TestSpecificationList, { description: 'To fetch test specifications' })\n async testSpecifications(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<TestSpecificationList> {\n const { domain } = context.state\n const { items, total } = await listMaster('testSpecification', params, domain)\n\n return { items: items as TestSpecification[], total }\n }\n\n /**\n * 그리드가 보낸 변경을 한 번에 적용한다.\n *\n * 패치를 `ScalarObject` 로 받는다 — 축마다 입력 타입을 따로 만들면 칸을 하나 더할 때마다 두 곳을\n * 고쳐야 하고, 한쪽을 빠뜨리면 그 칸이 오류 보고 없이 저장되지 않는다. 돌려주는 것은 타입이 있는\n * 엔티티라 무엇이 저장됐는지 스키마로 확인할 수 있다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecification], { description: 'To create, change or retire test specifications' })\n async updateMultipleTestSpecification(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecification[]> {\n const { domain, user, tx } = context.state\n const axis = masterAxisOf('testSpecification')\n\n const prepared = patches.map(patch => prepareCriteria(patch))\n\n const saved = await saveMasterPatches(tx, domain, axis.entity, prepared, user, {\n hasEffectivePeriod: axis.hasEffectivePeriod\n })\n\n return saved as TestSpecification[]\n }\n /**\n * 명세 하나의 기준을 **행으로** 낸다.\n *\n * 화면이 JSON 을 풀지 않게 하는 자리다. 명세를 못 찾으면 빈 목록을 주지 않고 말한다 — 빈 목록은\n * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [TestSpecificationCriterionRow], { description: 'Criteria of one test specification, as editable rows' })\n async testSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n /* 조회에는 `@transaction` 이 없다 — `context.state.tx` 는 그 지시자가 있을 때만 있다. */\n const { domain } = context.state\n\n const spec = await getRepository(TestSpecification).findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n return toCriterionRows(spec.criteria ?? []) as TestSpecificationCriterionRow[]\n }\n\n /**\n * 기준을 **바뀐 줄만** 받아 얹는다.\n *\n * 목록 전체를 받으면 두 사람이 같은 명세를 열어 각자 한 줄씩 고쳤을 때 나중에 저장한 쪽이 앞사람의\n * 줄을 지운다. 판단은 `criteria-rows.ts` 와 `criteria-check.ts` 가 든다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecificationCriterionRow], { description: 'To add, change or remove criteria of one test specification' })\n async updateTestSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Arg('patches', type => [ScalarObject], { description: 'changed rows only, each with cuFlag and rowIndex' })\n patches: CriterionPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(TestSpecification)\n\n const spec = await repository.findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n const applied = applyCriterionPatches(spec.criteria ?? [], patches)\n\n if (applied.applied === 'rejected') {\n /* 무엇이 왜 막혔는지 다 낸다 — 한 줄씩 고치게 하면 저장을 여러 번 되풀이하게 된다. */\n throw new Error(\n `검사 명세 \"${name}\" 의 기준을 저장하지 않았습니다.\\n` + applied.problems.map(one => `· ${one}`).join('\\n')\n )\n }\n\n await repository.save({ ...spec, criteria: applied.criteria, updater: user } as any)\n\n return toCriterionRows(applied.criteria) as TestSpecificationCriterionRow[]\n }\n}\n"]}
1
+ {"version":3,"file":"test-spec-master-resolver.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-master-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Args, Arg, Ctx, Directive, Float, Int, Mutation, ObjectType, Field, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { ListParam, ScalarObject, getRepository, SpeakRefusals } from '@things-factory/shell'\n\nimport type { MasterPatch } from '../master-crud'\nimport { CriterionPatch, toCriterionRows } from './criteria-rows'\nimport { TestSpecification } from './test-specification'\nimport { effectiveTestSpecification, TestSpecificationRefusal } from './test-spec-revision-service'\nimport { listEffectiveTestSpecifications } from './test-spec-effective-list'\n\n/** Reads project the effective revision. Changes go through the revision draft resolver. */\n\n/**\n * 기준 한 줄 — **화면이 받는 모양.**\n *\n * 한계를 펴서 낸다. 화면은 이름 · 설명 · 하한 · 상한 · 단위 · 표현식이 각자 칸인 표를 그리고,\n * JSON 을 아예 모른다. 옮김은 `criteria-rows.ts` 한 곳이 든다 — 저장이 표로 내려가도 그 파일만 바뀐다.\n */\n@ObjectType({ description: 'One test specification criterion, flattened for editing' })\nexport class TestSpecificationCriterionRow {\n @Field({ description: 'criterion id — measurements find their criterion by this' })\n id: string\n\n @Field({ nullable: true, description: 'what is being checked' })\n description?: string\n\n @Field({ nullable: true, description: 'ISA-95 EvaluatedPropertyID — which property is measured' })\n evaluatedPropertyId?: string\n\n @Field(type => Float, { nullable: true, description: 'lower limit' })\n minimum?: number\n\n @Field(type => Float, { nullable: true, description: 'upper limit' })\n maximum?: number\n\n @Field({ nullable: true, description: 'unit of measure — required whenever a limit is given' })\n uom?: string\n\n @Field({ nullable: true, description: \"the site's own notation — never parsed\" })\n expression?: string\n\n /**\n * 화면이 아직 안 그리는 표준 칸들.\n *\n * **버리지 않는다.** 편 것을 다시 담을 때 이 칸들이 사라지면 사람이 적은 것이 조용히 없어진다.\n */\n @Field(type => ScalarObject, { nullable: true, description: 'standard fields this screen does not draw yet' })\n keptFields?: Record<string, unknown>\n}\n\n@ObjectType({ description: 'A page of test specifications' })\nexport class TestSpecificationList {\n @Field(type => [TestSpecification])\n items: TestSpecification[]\n\n @Field(type => Int)\n total: number\n}\n\n@Resolver(TestSpecification)\n@UseMiddleware(SpeakRefusals)\nexport class TestSpecificationResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => TestSpecificationList, { description: 'To fetch test specifications' })\n async testSpecifications(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<TestSpecificationList> {\n const { domain } = context.state\n return listEffectiveTestSpecifications(getRepository(TestSpecification).manager, domain, params)\n }\n\n /** Explicitly closed old write door: even non-regulated publication must retain a revision. */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecification], { description: 'To create, change or retire test specifications' })\n async updateMultipleTestSpecification(\n @Arg('patches', type => [ScalarObject]) patches: MasterPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecification[]> {\n throw new TestSpecificationRefusal('REVISION_REQUIRED', 'Use a test specification draft; direct identity-row changes are not supported.')\n }\n /**\n * 명세 하나의 기준을 **행으로** 낸다.\n *\n * 화면이 JSON 을 풀지 않게 하는 자리다. 명세를 못 찾으면 빈 목록을 주지 않고 말한다 — 빈 목록은\n * 「기준이 없는 명세」와 같아 보이고, 그러면 사람이 기준을 다시 적는다.\n */\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [TestSpecificationCriterionRow], { description: 'Criteria of one test specification, as editable rows' })\n async testSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n /* 조회에는 `@transaction` 이 없다 — `context.state.tx` 는 그 지시자가 있을 때만 있다. */\n const { domain } = context.state\n\n const spec = await getRepository(TestSpecification).findOne({ where: { domain: { id: domain.id }, name } })\n\n if (!spec) {\n throw new Error(`검사 명세 \"${name}\" 를 찾지 못했습니다`)\n }\n\n const effective = await effectiveTestSpecification(getRepository(TestSpecification).manager, domain.id, spec)\n return toCriterionRows(effective.criteria ?? []) as TestSpecificationCriterionRow[]\n }\n\n /** Criteria belong to a draft, not to the stable identity row. */\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => [TestSpecificationCriterionRow], { description: 'To add, change or remove criteria of one test specification' })\n async updateTestSpecificationCriteria(\n @Arg('name', { description: 'the test specification name' }) name: string,\n @Arg('patches', type => [ScalarObject], { description: 'changed rows only, each with cuFlag and rowIndex' })\n patches: CriterionPatch[],\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationCriterionRow[]> {\n throw new TestSpecificationRefusal('REVISION_REQUIRED', 'Edit the draft criteria, then submit the frozen revision for approval.')\n }\n}\n"]}
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TestSpecificationQuery = exports.TestSpecificationMaster = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
6
7
  const master_page_query_1 = require("../master-page-query");
7
8
  const test_spec_master_1 = require("./test-spec-master");
8
9
  const test_specification_1 = require("./test-specification");
10
+ const test_spec_effective_list_1 = require("./test-spec-effective-list");
9
11
  let TestSpecificationMaster = class TestSpecificationMaster {
10
12
  };
11
13
  exports.TestSpecificationMaster = TestSpecificationMaster;
@@ -32,8 +34,12 @@ let TestSpecificationQuery = class TestSpecificationQuery {
32
34
  const page = await (0, master_page_query_1.pullPage)(test_specification_1.TestSpecification, domain, request);
33
35
  /* 통째로 달라고 했는데 한도를 넘으면 **자르지 않고 거절한다**(§`master-page`). */
34
36
  (0, master_page_query_1.refuseIfTooLarge)('검사 명세', request, page);
37
+ const manager = (0, shell_1.getRepository)(test_specification_1.TestSpecification).manager;
38
+ const effective = page.rows.length ? (await (0, test_spec_effective_list_1.listEffectiveTestSpecifications)(manager, domain, {
39
+ filters: [{ name: 'id', operator: 'in', value: page.rows.map(row => row.id) }], sortings: [{ name: 'name', desc: false }]
40
+ })).items.filter(row => row.effectiveStart != null) : [];
35
41
  return {
36
- testSpecifications: JSON.stringify((0, test_spec_master_1.testSpecificationMaster)(page.rows).testSpecifications),
42
+ testSpecifications: JSON.stringify((0, test_spec_master_1.testSpecificationMaster)(effective).testSpecifications),
37
43
  hasMore: page.hasMore,
38
44
  ...(page.next ? { next: page.next } : {})
39
45
  };
@@ -1 +1 @@
1
- {"version":3,"file":"test-spec-query.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAgF;AAIhF,4DAAiE;AAEjE,yDAA4F;AAC5F,6DAAwD;AAGjD,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;CAgBnC,CAAA;AAhBY,0DAAuB;AAElC;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;;mEACjD;AAQ1B;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;;wDACvE;AAIhB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;;qDACnG;kCAdF,uBAAuB;IADnC,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;GAChE,uBAAuB,CAgBnC;AAGM,IAAM,sBAAsB,GAA5B,MAAM,sBAAsB;IACjC,iDAAiD;IAE3C,AAAN,KAAK,CAAC,uBAAuB,CAC0E,KAAyB,EACO,KAAyB,EACvJ,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAEhC,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;QAChC,MAAM,IAAI,GAAG,MAAM,IAAA,4BAAQ,EAAoB,sCAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAElF,0DAA0D;QAC1D,IAAA,oCAAgB,EAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAA;QAExC,OAAO;YACL,kBAAkB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAA,0CAA4B,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC,kBAAkB,CAAC;YAC9F,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAA;IACH,CAAC;CACF,CAAA;AAtBY,wDAAsB;AAG3B;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,uBAAuB,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC;IAEpG,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAA;IACnG,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,uEAAuE,EAAE,CAAC,CAAA;IACnI,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;qEAeP;iCArBU,sBAAsB;IADlC,IAAA,uBAAQ,EAAC,sCAAiB,CAAC;GACf,sBAAsB,CAsBlC","sourcesContent":["import { Arg, Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { pullPage, refuseIfTooLarge } from '../master-page-query'\n\nimport { testSpecificationMaster as buildTestSpecificationMaster } from './test-spec-master'\nimport { TestSpecification } from './test-specification'\n\n@ObjectType({ description: 'Test specifications as the twin receives them' })\nexport class TestSpecificationMaster {\n @Field(type => String, { description: 'TestSpecification[] as JSON text' })\n testSpecifications: string\n\n /**\n * 더 있나 — **이 값을 안 보고 쓰면 조용히 잘린 마스터로 트윈을 세우게 된다.**\n *\n * 거짓이 될 때까지 `next` 를 `after` 로 넘겨 이어 받는다.\n */\n @Field({ description: 'true when more rows remain — keep pulling with next as after' })\n hasMore: boolean\n\n /** 이어 받을 자리. 더 없으면 없다. */\n @Field(type => String, { nullable: true, description: 'cursor for the next page — the last name in this page' })\n next?: string\n\n}\n\n@Resolver(TestSpecification)\nexport class TestSpecificationQuery {\n /** `TestSpecification` 전체 — 자원의 참조가 가리킬 대상이다. */\n @Query(returns => TestSpecificationMaster, { description: 'Test specifications for the twin to pull' })\n async testSpecificationMaster(\n @Arg('after', { nullable: true, description: 'resume after this name — the previous page\\'s next' }) after: string | undefined,\n @Arg('limit', type => Int, { nullable: true, description: 'rows per page — omit only when the axis is small enough to pull whole' }) limit: number | undefined,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationMaster> {\n const { domain } = context.state\n\n const request = { after, limit }\n const page = await pullPage<TestSpecification>(TestSpecification, domain, request)\n\n /* 통째로 달라고 했는데 한도를 넘으면 **자르지 않고 거절한다**(§`master-page`). */\n refuseIfTooLarge('검사 명세', request, page)\n\n return {\n testSpecifications: JSON.stringify(buildTestSpecificationMaster(page.rows).testSpecifications),\n hasMore: page.hasMore,\n ...(page.next ? { next: page.next } : {})\n }\n }\n}\n"]}
1
+ {"version":3,"file":"test-spec-query.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { pullPage, refuseIfTooLarge } from '../master-page-query'\n\nimport { testSpecificationMaster as buildTestSpecificationMaster } from './test-spec-master'\nimport { TestSpecification } from './test-specification'\nimport { listEffectiveTestSpecifications } from './test-spec-effective-list'\n\n@ObjectType({ description: 'Test specifications as the twin receives them' })\nexport class TestSpecificationMaster {\n @Field(type => String, { description: 'TestSpecification[] as JSON text' })\n testSpecifications: string\n\n /**\n * 더 있나 — **이 값을 안 보고 쓰면 조용히 잘린 마스터로 트윈을 세우게 된다.**\n *\n * 거짓이 될 때까지 `next` 를 `after` 로 넘겨 이어 받는다.\n */\n @Field({ description: 'true when more rows remain — keep pulling with next as after' })\n hasMore: boolean\n\n /** 이어 받을 자리. 더 없으면 없다. */\n @Field(type => String, { nullable: true, description: 'cursor for the next page — the last name in this page' })\n next?: string\n\n}\n\n@Resolver(TestSpecification)\nexport class TestSpecificationQuery {\n /** `TestSpecification` 전체 — 자원의 참조가 가리킬 대상이다. */\n @Query(returns => TestSpecificationMaster, { description: 'Test specifications for the twin to pull' })\n async testSpecificationMaster(\n @Arg('after', { nullable: true, description: 'resume after this name — the previous page\\'s next' }) after: string | undefined,\n @Arg('limit', type => Int, { nullable: true, description: 'rows per page — omit only when the axis is small enough to pull whole' }) limit: number | undefined,\n @Ctx() context: ResolverContext\n ): Promise<TestSpecificationMaster> {\n const { domain } = context.state\n\n const request = { after, limit }\n const page = await pullPage<TestSpecification>(TestSpecification, domain, request)\n\n /* 통째로 달라고 했는데 한도를 넘으면 **자르지 않고 거절한다**(§`master-page`). */\n refuseIfTooLarge('검사 명세', request, page)\n\n const manager = getRepository(TestSpecification).manager\n const effective = page.rows.length ? (await listEffectiveTestSpecifications(manager, domain, {\n filters: [{ name: 'id', operator: 'in', value: page.rows.map(row => row.id) }], sortings: [{ name: 'name', desc: false }]\n })).items.filter(row => row.effectiveStart != null) : []\n return {\n testSpecifications: JSON.stringify(buildTestSpecificationMaster(effective).testSpecifications),\n hasMore: page.hasMore,\n ...(page.next ? { next: page.next } : {})\n }\n }\n}\n"]}
@@ -0,0 +1,52 @@
1
+ import { TestSpecification } from './test-specification';
2
+ import { TestSpecificationDraftContent } from './test-spec-revision-service';
3
+ export declare class TestSpecificationRevisionResolver {
4
+ publishTestSpecificationDraft(revisionId: string, editGeneration: number, context: ResolverContext): Promise<import("./test-spec-revision").TestSpecificationPlan>;
5
+ testSpecificationRevisions(specId: string, context: ResolverContext): Promise<{
6
+ specId: string;
7
+ name: string;
8
+ generation: number;
9
+ at: string;
10
+ schedule: {
11
+ state: string;
12
+ id: string;
13
+ domain: import("@things-factory/shell").Domain;
14
+ domainId: string;
15
+ spec: TestSpecification;
16
+ specId: string;
17
+ plan: import("./test-spec-revision").TestSpecificationPlan;
18
+ planId: string;
19
+ revision: import("./test-spec-revision").TestSpecificationRevision;
20
+ revisionId: string;
21
+ validFrom: Date;
22
+ validTo: Date | null;
23
+ }[];
24
+ revisions: {
25
+ state: string;
26
+ policy: import("..").MasterChangePolicy;
27
+ policyId: string;
28
+ id: string;
29
+ domain: import("@things-factory/shell").Domain;
30
+ domainId: string;
31
+ spec: TestSpecification;
32
+ specId: string;
33
+ baseRevision: import("./test-spec-revision").TestSpecificationRevision | null;
34
+ baseRevisionId: string | null;
35
+ baseGeneration: number;
36
+ editGeneration: number;
37
+ version: string;
38
+ description: string | null;
39
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
40
+ effectiveFrom: Date;
41
+ reason: string;
42
+ creator: import("@things-factory/auth-base").User;
43
+ creatorId: string;
44
+ createdAt: Date;
45
+ frozenAt: Date | null;
46
+ contentHash: string | null;
47
+ activityInstanceId: string | null;
48
+ }[];
49
+ }>;
50
+ createTestSpecificationDraft(content: TestSpecificationDraftContent, specId: string, name: string, baseRevisionId: string, context: ResolverContext): Promise<import("./test-spec-revision").TestSpecificationRevision>;
51
+ editTestSpecificationDraft(revisionId: string, editGeneration: number, content: TestSpecificationDraftContent, context: ResolverContext): Promise<import("./test-spec-revision").TestSpecificationRevision>;
52
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestSpecificationRevisionResolver = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const test_specification_1 = require("./test-specification");
8
+ const test_spec_revision_service_1 = require("./test-spec-revision-service");
9
+ let TestSpecificationRevisionResolver = class TestSpecificationRevisionResolver {
10
+ async publishTestSpecificationDraft(revisionId, editGeneration, context) {
11
+ const { tx, domain, user } = context.state;
12
+ return (0, test_spec_revision_service_1.publishUnregulatedTestSpecification)(tx, domain.id, user.id, revisionId, editGeneration);
13
+ }
14
+ async testSpecificationRevisions(specId, context) {
15
+ return (0, test_spec_revision_service_1.testSpecificationRevisionOverview)((0, shell_1.getRepository)(test_specification_1.TestSpecification).manager, context.state.domain.id, specId);
16
+ }
17
+ async createTestSpecificationDraft(content, specId, name, baseRevisionId, context) {
18
+ const { tx, domain, user } = context.state;
19
+ return (0, test_spec_revision_service_1.createTestSpecificationDraft)(tx, domain.id, user.id, { specId, name, baseRevisionId, content });
20
+ }
21
+ async editTestSpecificationDraft(revisionId, editGeneration, content, context) {
22
+ const { tx, domain, user } = context.state;
23
+ return (0, test_spec_revision_service_1.editTestSpecificationDraft)(tx, domain.id, user.id, revisionId, editGeneration, content);
24
+ }
25
+ };
26
+ exports.TestSpecificationRevisionResolver = TestSpecificationRevisionResolver;
27
+ tslib_1.__decorate([
28
+ (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
29
+ (0, type_graphql_1.Directive)('@transaction'),
30
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
31
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('revisionId')),
32
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('editGeneration', () => type_graphql_1.Int)),
33
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
34
+ tslib_1.__metadata("design:type", Function),
35
+ tslib_1.__metadata("design:paramtypes", [String, Number, Object]),
36
+ tslib_1.__metadata("design:returntype", Promise)
37
+ ], TestSpecificationRevisionResolver.prototype, "publishTestSpecificationDraft", null);
38
+ tslib_1.__decorate([
39
+ (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "query", domainOwnerGranted: true)'),
40
+ (0, type_graphql_1.Query)(() => shell_1.ScalarObject),
41
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('specId')),
42
+ tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
43
+ tslib_1.__metadata("design:type", Function),
44
+ tslib_1.__metadata("design:paramtypes", [String, Object]),
45
+ tslib_1.__metadata("design:returntype", Promise)
46
+ ], TestSpecificationRevisionResolver.prototype, "testSpecificationRevisions", null);
47
+ tslib_1.__decorate([
48
+ (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
49
+ (0, type_graphql_1.Directive)('@transaction'),
50
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
51
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('content', () => shell_1.ScalarObject)),
52
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('specId', () => String, { nullable: true })),
53
+ tslib_1.__param(2, (0, type_graphql_1.Arg)('name', () => String, { nullable: true })),
54
+ tslib_1.__param(3, (0, type_graphql_1.Arg)('baseRevisionId', () => String, { nullable: true })),
55
+ tslib_1.__param(4, (0, type_graphql_1.Ctx)()),
56
+ tslib_1.__metadata("design:type", Function),
57
+ tslib_1.__metadata("design:paramtypes", [Object, String, String, String, Object]),
58
+ tslib_1.__metadata("design:returntype", Promise)
59
+ ], TestSpecificationRevisionResolver.prototype, "createTestSpecificationDraft", null);
60
+ tslib_1.__decorate([
61
+ (0, type_graphql_1.Directive)('@privilege(category: "ops-master", privilege: "mutation", domainOwnerGranted: true)'),
62
+ (0, type_graphql_1.Directive)('@transaction'),
63
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
64
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('revisionId')),
65
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('editGeneration', () => type_graphql_1.Int)),
66
+ tslib_1.__param(2, (0, type_graphql_1.Arg)('content', () => shell_1.ScalarObject)),
67
+ tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
68
+ tslib_1.__metadata("design:type", Function),
69
+ tslib_1.__metadata("design:paramtypes", [String, Number, Object, Object]),
70
+ tslib_1.__metadata("design:returntype", Promise)
71
+ ], TestSpecificationRevisionResolver.prototype, "editTestSpecificationDraft", null);
72
+ exports.TestSpecificationRevisionResolver = TestSpecificationRevisionResolver = tslib_1.__decorate([
73
+ (0, type_graphql_1.Resolver)(),
74
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
75
+ ], TestSpecificationRevisionResolver);
76
+ //# sourceMappingURL=test-spec-revision-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-spec-revision-resolver.js","sourceRoot":"","sources":["../../../server/service/test-spec/test-spec-revision-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { getRepository, ScalarObject, SpeakRefusals } from '@things-factory/shell'\nimport { TestSpecification } from './test-specification'\nimport { createTestSpecificationDraft, editTestSpecificationDraft, publishUnregulatedTestSpecification, testSpecificationRevisionOverview, TestSpecificationDraftContent } from './test-spec-revision-service'\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class TestSpecificationRevisionResolver {\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async publishTestSpecificationDraft(@Arg('revisionId') revisionId: string, @Arg('editGeneration', () => Int) editGeneration: number, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return publishUnregulatedTestSpecification(tx, domain.id, user.id, revisionId, editGeneration)\n }\n @Directive('@privilege(category: \"ops-master\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async testSpecificationRevisions(@Arg('specId') specId: string, @Ctx() context: ResolverContext) {\n return testSpecificationRevisionOverview(getRepository(TestSpecification).manager, context.state.domain.id, specId)\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async createTestSpecificationDraft(\n @Arg('content', () => ScalarObject) content: TestSpecificationDraftContent,\n @Arg('specId', () => String, { nullable: true }) specId: string,\n @Arg('name', () => String, { nullable: true }) name: string,\n @Arg('baseRevisionId', () => String, { nullable: true }) baseRevisionId: string,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return createTestSpecificationDraft(tx, domain.id, user.id, { specId, name, baseRevisionId, content })\n }\n\n @Directive('@privilege(category: \"ops-master\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async editTestSpecificationDraft(\n @Arg('revisionId') revisionId: string,\n @Arg('editGeneration', () => Int) editGeneration: number,\n @Arg('content', () => ScalarObject) content: TestSpecificationDraftContent,\n @Ctx() context: ResolverContext\n ) {\n const { tx, domain, user } = context.state\n return editTestSpecificationDraft(tx, domain.id, user.id, revisionId, editGeneration, content)\n }\n}\n"]}
@@ -0,0 +1,117 @@
1
+ import { EntityManager } from 'typeorm';
2
+ import { Refusal } from '@things-factory/shell';
3
+ import { TestSpecification } from './test-specification';
4
+ import { TestSpecificationBasis, TestSpecificationPlan, TestSpecificationRevision, TestSpecificationSegment } from './test-spec-revision';
5
+ export declare class TestSpecificationRefusal extends Refusal {
6
+ constructor(code: string, detail?: string);
7
+ }
8
+ export declare function testSpecificationSubmissionHash(revision: TestSpecificationRevision): string;
9
+ /** Display values are a checked projection of the frozen revision, never a second editable source. */
10
+ export declare function testSpecificationApprovalInput(manager: EntityManager, domainId: string, revisionId: string): Promise<{
11
+ headline: string;
12
+ reason: string;
13
+ effectiveFrom: string;
14
+ before: {
15
+ version: string;
16
+ description: string;
17
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
18
+ };
19
+ after: {
20
+ version: string;
21
+ description: string;
22
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
23
+ };
24
+ revision: {
25
+ revisionId: string;
26
+ hash: string;
27
+ specId: string;
28
+ baseGeneration: number;
29
+ };
30
+ }>;
31
+ export declare function assertTestSpecificationApprovalInput(manager: EntityManager, domainId: string, input: Record<string, unknown>): Promise<void>;
32
+ export interface TestSpecificationDraftContent {
33
+ version: string;
34
+ description?: string;
35
+ criteria: unknown;
36
+ effectiveFrom: Date | string;
37
+ reason: string;
38
+ }
39
+ export declare function createTestSpecificationDraft(manager: EntityManager, domainId: string, creatorId: string, asked: {
40
+ specId?: string;
41
+ name?: string;
42
+ baseRevisionId?: string;
43
+ content: TestSpecificationDraftContent;
44
+ }): Promise<TestSpecificationRevision>;
45
+ export declare function editTestSpecificationDraft(manager: EntityManager, domainId: string, actorId: string, revisionId: string, expectedEditGeneration: number, input: TestSpecificationDraftContent): Promise<TestSpecificationRevision>;
46
+ /** Product calls this in the same transaction as issuing its worklist approval. */
47
+ export declare function freezeTestSpecificationDraft(manager: EntityManager, domainId: string, actorId: string, revisionId: string, expectedEditGeneration: number): Promise<TestSpecificationRevision>;
48
+ export declare function bindTestSpecificationApproval(manager: EntityManager, domainId: string, revisionId: string, activityInstanceId: string): Promise<void>;
49
+ /** Internal completion callback only. Never expose this as an approved=true mutation. */
50
+ export declare function publishTestSpecificationRevision(manager: EntityManager, domainId: string, revisionId: string, decision: {
51
+ activityInstanceId: string;
52
+ approverId: string;
53
+ submissionHash: string;
54
+ }): Promise<TestSpecificationPlan>;
55
+ export declare function publishUnregulatedTestSpecification(manager: EntityManager, domainId: string, actorId: string, revisionId: string, editGeneration: number): Promise<TestSpecificationPlan>;
56
+ export declare function resolveTestSpecification(manager: EntityManager, domainId: string, specId: string, businessTime: Date | string, knownAt?: Date | string): Promise<{
57
+ revision: TestSpecificationRevision;
58
+ plan: TestSpecificationPlan;
59
+ segment: TestSpecificationSegment;
60
+ }>;
61
+ /** Replay reads verify the captured digest too, not only the currently effective schedule. */
62
+ export declare function testSpecificationRevisionForBasis(manager: EntityManager, domainId: string, basis: TestSpecificationBasis): Promise<TestSpecificationRevision>;
63
+ /** Must run inside the transaction that writes the inspection result. */
64
+ export declare function captureTestSpecificationBasis(manager: EntityManager, domainId: string, specId: string, recordKey: string, businessTime: Date | string): Promise<TestSpecificationBasis>;
65
+ export declare function testSpecificationRevisionOverview(manager: EntityManager, domainId: string, specId: string, at?: Date): Promise<{
66
+ specId: string;
67
+ name: string;
68
+ generation: number;
69
+ at: string;
70
+ schedule: {
71
+ state: string;
72
+ id: string;
73
+ domain: import("@things-factory/shell").Domain;
74
+ domainId: string;
75
+ spec: TestSpecification;
76
+ specId: string;
77
+ plan: TestSpecificationPlan;
78
+ planId: string;
79
+ revision: TestSpecificationRevision;
80
+ revisionId: string;
81
+ validFrom: Date;
82
+ validTo: Date | null;
83
+ }[];
84
+ revisions: {
85
+ state: string;
86
+ policy: import("../master-change-policy").MasterChangePolicy;
87
+ policyId: string;
88
+ id: string;
89
+ domain: import("@things-factory/shell").Domain;
90
+ domainId: string;
91
+ spec: TestSpecification;
92
+ specId: string;
93
+ baseRevision: TestSpecificationRevision | null;
94
+ baseRevisionId: string | null;
95
+ baseGeneration: number;
96
+ editGeneration: number;
97
+ version: string;
98
+ description: string | null;
99
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
100
+ effectiveFrom: Date;
101
+ reason: string;
102
+ creator: import("@things-factory/auth-base").User;
103
+ creatorId: string;
104
+ createdAt: Date;
105
+ frozenAt: Date | null;
106
+ contentHash: string | null;
107
+ activityInstanceId: string | null;
108
+ }[];
109
+ }>;
110
+ /** Read-time projection, never a clock-driven rewrite of the identity row. */
111
+ export declare function effectiveTestSpecification(manager: EntityManager, domainId: string, spec: TestSpecification, at?: Date): Promise<TestSpecification & {
112
+ version: string;
113
+ description: string;
114
+ criteria: import("@operato/ops-contract").TestSpecificationCriterion[];
115
+ effectiveStart: Date;
116
+ effectiveEnd: Date;
117
+ }>;