dalux-build-api 1.1.5 → 2.0.0

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/src/index.js CHANGED
@@ -39,6 +39,8 @@ const {
39
39
  resolveFolderIdFromNamedPath,
40
40
  } = require('./utils');
41
41
 
42
+ const models = require('./models');
43
+
42
44
  /**
43
45
  * Create a fully configured Dalux Build API client.
44
46
  *
@@ -125,4 +127,7 @@ module.exports = {
125
127
  validateFolderId,
126
128
  resolveFileAreaByName,
127
129
  resolveFolderIdFromNamedPath,
130
+ // Data models (zod schemas) - both `models.FolderSchema` and top-level `FolderSchema` work
131
+ models,
132
+ ...models,
128
133
  };
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * API response link. Mirrors python/dalux_build/models/common.py::Link.
7
+ */
8
+ const LinkSchema = z.object({
9
+ rel: z.string(),
10
+ href: z.string(),
11
+ method: z.string().nullish(),
12
+ });
13
+
14
+ /**
15
+ * Response metadata with pagination info. Mirrors common.py::Metadata.
16
+ */
17
+ const MetadataSchema = z.object({
18
+ totalItems: z.number().nullish(),
19
+ totalRemainingItems: z.number().nullish(),
20
+ });
21
+
22
+ module.exports = { LinkSchema, MetadataSchema };
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ const { ProjectCompanySchema } = require('../projects');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/companies/models.py, which re-exports ProjectCompany. */
7
+ const CompaniesListResponseSchema = listResponseSchema(ProjectCompanySchema);
8
+ const CompanyResponseSchema = singleResponseSchema(ProjectCompanySchema);
9
+
10
+ module.exports = {
11
+ ProjectCompanySchema,
12
+ CompaniesListResponseSchema,
13
+ CompanyResponseSchema,
14
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /**
7
+ * Mirrors models/company_catalog/responses.py. These are exported for name
8
+ * parity but, like their Python counterparts, are NOT what the API layer
9
+ * actually uses — company_catalog.py's get_companies/get_company return
10
+ * CompaniesListResponse/CompanyResponse (typed ProjectCompany) instead.
11
+ * See src/api/CompanyCatalogApi.js.
12
+ */
13
+ const CompanyCatalogListResponseSchema = listResponseSchema(z.any());
14
+ const CompanyCatalogResponseSchema = singleResponseSchema(z.any());
15
+
16
+ module.exports = {
17
+ CompanyCatalogListResponseSchema,
18
+ CompanyCatalogResponseSchema,
19
+ };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { ValidationError } = require('../utils/errors');
5
+ const { unwrapData } = require('./helpers');
6
+
7
+ /**
8
+ * Validate and convert a raw API response into a typed model, throwing a
9
+ * ValidationError if the response doesn't match `schema`. Mirrors
10
+ * response_converter.py::convert_to_model, without its legacy-compat
11
+ * fallback branches (the JS test suite ships schema-valid fixtures).
12
+ * @param {*} response
13
+ * @param {import('zod').ZodTypeAny} schema
14
+ * @param {string} [schemaName]
15
+ * @returns {*}
16
+ */
17
+ function convertToModel(response, schema, schemaName = 'model') {
18
+ if (response === null || response === undefined) return null;
19
+ try {
20
+ return schema.parse(response);
21
+ } catch (err) {
22
+ if (err instanceof z.ZodError) {
23
+ throw new ValidationError(`Failed to convert response to ${schemaName}: ${err.message}`);
24
+ }
25
+ throw err;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Validate and convert a list of raw items into typed models.
31
+ * Used by get_all_* pagination methods, which convert each item after
32
+ * pagination has already collected the raw list.
33
+ * @param {Array} items
34
+ * @param {import('zod').ZodTypeAny} itemSchema
35
+ * @param {string} [schemaName]
36
+ * @returns {Array}
37
+ */
38
+ function convertToModelList(items, itemSchema, schemaName = 'model') {
39
+ if (!Array.isArray(items)) return [];
40
+ const wrapped = unwrapData(itemSchema);
41
+ return items.map((item) => convertToModel(item, wrapped, schemaName));
42
+ }
43
+
44
+ module.exports = { convertToModel, convertToModelList };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/file_areas/models.py::FileArea (all fields required). */
7
+ const FileAreaSchema = z.object({
8
+ fileAreaId: z.string(),
9
+ fileAreaName: z.string(),
10
+ fileAreaType: z.string(),
11
+ });
12
+
13
+ const FileAreasListResponseSchema = listResponseSchema(FileAreaSchema);
14
+ const FileAreaResponseSchema = singleResponseSchema(FileAreaSchema);
15
+
16
+ module.exports = {
17
+ FileAreaSchema,
18
+ FileAreasListResponseSchema,
19
+ FileAreaResponseSchema,
20
+ };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * Mirrors models/file_revisions/models.py::FileRevision — an empty
7
+ * placeholder in Python too (get_file_revision_content returns raw
8
+ * binary/content in both packages, so there's nothing to validate).
9
+ */
10
+ const FileRevisionSchema = z.object({}).passthrough();
11
+
12
+ module.exports = { FileRevisionSchema };
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+
5
+ /**
6
+ * Mirrors models/file_upload/models.py::FileUpload — empty placeholder in
7
+ * Python too; upload/part/finalize responses are provider-specific and
8
+ * stay raw in both packages.
9
+ */
10
+ const FileUploadSchema = z.object({}).passthrough();
11
+
12
+ module.exports = { FileUploadSchema };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema, nullableDefault } = require('../helpers');
5
+
6
+ /** Mirrors models/files/models.py::Reference. */
7
+ const ReferenceSchema = z.object({
8
+ key: z.string(),
9
+ value: z.string(),
10
+ });
11
+
12
+ /** Mirrors models/files/models.py::FileIntegerProperty. */
13
+ const FileIntegerPropertySchema = z.object({
14
+ integer: z.number().nullish(),
15
+ });
16
+
17
+ /** Mirrors models/files/models.py::FileDateProperty. */
18
+ const FileDatePropertySchema = z.object({
19
+ date: z.string().nullish(),
20
+ });
21
+
22
+ /** Mirrors models/files/models.py::FileTextProperty. */
23
+ const FileTextPropertySchema = z.object({
24
+ text: z.string().nullish(),
25
+ });
26
+
27
+ /** Mirrors models/files/models.py::FileReferenceProperty. */
28
+ const FileReferencePropertySchema = z.object({
29
+ reference: ReferenceSchema.nullish(),
30
+ });
31
+
32
+ /** Mirrors models/files/models.py::FilePropertyField. */
33
+ const FilePropertyFieldSchema = z.object({
34
+ key: z.string(),
35
+ name: z.string(),
36
+ values: z.array(z.any()).nullish(),
37
+ });
38
+
39
+ /**
40
+ * Mirrors models/files/models.py::FileNameFilter. Client-side filter params
41
+ * (not part of any API response), so field names stay plain camelCase with
42
+ * no aliasing concerns on either side.
43
+ */
44
+ const FileNameFilterSchema = z.object({
45
+ contains: z.array(z.string()).optional(),
46
+ containsMatch: z.enum(['any', 'all']).default('any'),
47
+ notContains: z.array(z.string()).optional(),
48
+ startswith: z.array(z.string()).optional(),
49
+ notStartswith: z.array(z.string()).optional(),
50
+ endswith: z.array(z.string()).optional(),
51
+ notEndswith: z.array(z.string()).optional(),
52
+ extensions: z.array(z.string()).optional(),
53
+ notExtensions: z.array(z.string()).optional(),
54
+ });
55
+
56
+ /**
57
+ * Mirrors models/files/models.py::File. `uploaded`/`lastModified` are kept
58
+ * as ISO date strings (not coerced to JS Date) so JSON round-tripping and
59
+ * existing string-based consumer code keeps working.
60
+ */
61
+ const FileSchema = z.object({
62
+ fileId: z.string(),
63
+ fileRevisionId: z.string().nullish(),
64
+ fileName: z.string(),
65
+ fileAreaId: z.string(),
66
+ folderId: z.string().nullish(),
67
+ uploadedByUserId: z.string().nullish(),
68
+ uploaded: z.string().nullish(),
69
+ lastModifiedByUserId: z.string().nullish(),
70
+ lastModified: z.string().nullish(),
71
+ version: z.string().nullish(),
72
+ deleted: nullableDefault(z.boolean(), false),
73
+ fileType: z.string().nullish(),
74
+ fileSize: z.number().nullish(),
75
+ contentHash: z.string().nullish(),
76
+ downloadLink: z.string().nullish(),
77
+ properties: z.array(FilePropertyFieldSchema).nullish(),
78
+ // Local-only fields set by bulk-download helpers, not present in raw API responses.
79
+ savedFilePath: z.string().nullish(),
80
+ savedMetadataPath: z.string().nullish(),
81
+ });
82
+
83
+ const FilesListResponseSchema = listResponseSchema(FileSchema);
84
+ const FileResponseSchema = singleResponseSchema(FileSchema);
85
+
86
+ module.exports = {
87
+ ReferenceSchema,
88
+ FileIntegerPropertySchema,
89
+ FileDatePropertySchema,
90
+ FileTextPropertySchema,
91
+ FileReferencePropertySchema,
92
+ FilePropertyFieldSchema,
93
+ FileNameFilterSchema,
94
+ FileSchema,
95
+ FilesListResponseSchema,
96
+ FileResponseSchema,
97
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/folders/models.py::Folder. */
7
+ const FolderSchema = z.object({
8
+ folderId: z.string(),
9
+ folderName: z.string(),
10
+ parentFolderId: z.string().nullish(),
11
+ });
12
+
13
+ const FoldersListResponseSchema = listResponseSchema(FolderSchema);
14
+ const FolderResponseSchema = singleResponseSchema(FolderSchema);
15
+
16
+ module.exports = {
17
+ FolderSchema,
18
+ FoldersListResponseSchema,
19
+ FolderResponseSchema,
20
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /**
7
+ * Mirrors models/forms/models.py::Form — empty placeholder in Python, so
8
+ * items stay untyped.
9
+ */
10
+ const FormSchema = z.any();
11
+
12
+ const FormsListResponseSchema = listResponseSchema(FormSchema);
13
+ const FormResponseSchema = singleResponseSchema(FormSchema);
14
+
15
+ module.exports = {
16
+ FormSchema,
17
+ FormsListResponseSchema,
18
+ FormResponseSchema,
19
+ };
@@ -0,0 +1,62 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { LinkSchema, MetadataSchema } = require('./common');
5
+
6
+ /**
7
+ * Unwraps a `{ data: {...} }` wrapper before validating against `schema`.
8
+ * Bare objects (no `data` wrapper) pass straight through.
9
+ * Mirrors the `unwrap_and_convert_items` field_validator repeated across
10
+ * every Python `responses.py` file.
11
+ * @param {import('zod').ZodTypeAny} schema
12
+ */
13
+ function unwrapData(schema) {
14
+ return z.preprocess((value) => {
15
+ if (value && typeof value === 'object' && !Array.isArray(value) && 'data' in value) {
16
+ return value.data;
17
+ }
18
+ return value;
19
+ }, schema);
20
+ }
21
+
22
+ /**
23
+ * Builds a `{ items, metadata?, links? }` list-response schema.
24
+ * Accepts a bare array payload too (wraps it as `{ items: [...] }`) —
25
+ * mirrors TaskChanges' `wrap_list_payloads` model_validator.
26
+ * @param {import('zod').ZodTypeAny} itemSchema
27
+ */
28
+ function listResponseSchema(itemSchema) {
29
+ return z.preprocess(
30
+ (value) => (Array.isArray(value) ? { items: value } : value),
31
+ z.object({
32
+ items: z.array(unwrapData(itemSchema)).default([]),
33
+ metadata: MetadataSchema.optional(),
34
+ links: z.array(LinkSchema).optional(),
35
+ }),
36
+ );
37
+ }
38
+
39
+ /**
40
+ * Builds a `{ data, links? }` single-item response schema.
41
+ * @param {import('zod').ZodTypeAny} itemSchema
42
+ */
43
+ function singleResponseSchema(itemSchema) {
44
+ return z.object({
45
+ data: itemSchema,
46
+ links: z.array(LinkSchema).optional(),
47
+ });
48
+ }
49
+
50
+ /**
51
+ * A defaulted field that also accepts an explicit JSON `null` (treated the
52
+ * same as an absent field) — mirrors Pydantic's `Optional[T] = default`,
53
+ * which accepts both a missing key and an explicit `null`. Plain
54
+ * `schema.default(x)` alone only fires on `undefined`, not `null`.
55
+ * @param {import('zod').ZodTypeAny} schema
56
+ * @param {*} defaultValue
57
+ */
58
+ function nullableDefault(schema, defaultValue) {
59
+ return z.preprocess((v) => (v === null ? undefined : v), schema.default(defaultValue));
60
+ }
61
+
62
+ module.exports = { unwrapData, listResponseSchema, singleResponseSchema, nullableDefault };
@@ -0,0 +1,148 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Zod schemas for Dalux Build API responses. Mirrors the public surface of
5
+ * python/dalux_build/models/__init__.py, with a `Schema` suffix on every
6
+ * export name (these are zod schema objects, not constructible classes).
7
+ */
8
+
9
+ const { LinkSchema, MetadataSchema } = require('./common');
10
+
11
+ const {
12
+ ProjectSchema,
13
+ ProjectModuleSchema,
14
+ ProjectMetadataSchema,
15
+ ProjectTemplateSchema,
16
+ ProjectCompanySchema,
17
+ ProjectsListResponseSchema,
18
+ ProjectResponseSchema,
19
+ } = require('./projects');
20
+
21
+ const { FileAreaSchema, FileAreasListResponseSchema, FileAreaResponseSchema } = require('./fileAreas');
22
+
23
+ const { FolderSchema, FoldersListResponseSchema, FolderResponseSchema } = require('./folders');
24
+
25
+ const {
26
+ FileSchema,
27
+ ReferenceSchema,
28
+ FileNameFilterSchema,
29
+ FileIntegerPropertySchema,
30
+ FileDatePropertySchema,
31
+ FileTextPropertySchema,
32
+ FileReferencePropertySchema,
33
+ FilePropertyFieldSchema,
34
+ FilesListResponseSchema,
35
+ FileResponseSchema,
36
+ } = require('./files');
37
+
38
+ const { VersionSetSchema, VersionSetsListResponseSchema, VersionSetResponseSchema } = require('./versionSets');
39
+
40
+ const { UserSchema, ProjectUserSchema, UsersListResponseSchema, UserResponseSchema } = require('./users');
41
+
42
+ const { CompaniesListResponseSchema, CompanyResponseSchema } = require('./companies');
43
+
44
+ const { CompanyCatalogListResponseSchema, CompanyCatalogResponseSchema } = require('./companyCatalog');
45
+
46
+ const { InspectionPlanSchema, InspectionPlansListResponseSchema } = require('./inspectionPlans');
47
+
48
+ const { TestPlanSchema, TestPlansListResponseSchema } = require('./testPlans');
49
+
50
+ const { FormSchema, FormsListResponseSchema, FormResponseSchema } = require('./forms');
51
+
52
+ const {
53
+ TaskSchema,
54
+ TaskAttachmentSchema,
55
+ TaskChangeSchema,
56
+ TaskChangeActorSchema,
57
+ TaskChangeFieldsSchema,
58
+ TaskChangeLocationSchema,
59
+ TaskListParamsSchema,
60
+ TasksListResponseSchema,
61
+ TaskResponseSchema,
62
+ TaskChangeResponseSchema,
63
+ TaskChangesSchema,
64
+ TaskAttachmentsListResponseSchema,
65
+ } = require('./tasks');
66
+
67
+ const { FileRevisionSchema } = require('./fileRevisions');
68
+ const { FileUploadSchema } = require('./fileUpload');
69
+ const { WorkPackageSchema, WorkPackagesListResponseSchema } = require('./workPackages');
70
+
71
+ module.exports = {
72
+ // Base
73
+ LinkSchema,
74
+ MetadataSchema,
75
+ // Projects
76
+ ProjectSchema,
77
+ ProjectModuleSchema,
78
+ ProjectMetadataSchema,
79
+ ProjectTemplateSchema,
80
+ ProjectCompanySchema,
81
+ ProjectsListResponseSchema,
82
+ ProjectResponseSchema,
83
+ // File Areas
84
+ FileAreaSchema,
85
+ FileAreasListResponseSchema,
86
+ FileAreaResponseSchema,
87
+ // Folders
88
+ FolderSchema,
89
+ FoldersListResponseSchema,
90
+ FolderResponseSchema,
91
+ // Files
92
+ FileSchema,
93
+ ReferenceSchema,
94
+ FileNameFilterSchema,
95
+ FileIntegerPropertySchema,
96
+ FileDatePropertySchema,
97
+ FileTextPropertySchema,
98
+ FileReferencePropertySchema,
99
+ FilePropertyFieldSchema,
100
+ FilesListResponseSchema,
101
+ FileResponseSchema,
102
+ // Version Sets
103
+ VersionSetSchema,
104
+ VersionSetsListResponseSchema,
105
+ VersionSetResponseSchema,
106
+ // Users
107
+ UserSchema,
108
+ ProjectUserSchema,
109
+ UsersListResponseSchema,
110
+ UserResponseSchema,
111
+ // Companies
112
+ CompaniesListResponseSchema,
113
+ CompanyResponseSchema,
114
+ // Company Catalog
115
+ CompanyCatalogListResponseSchema,
116
+ CompanyCatalogResponseSchema,
117
+ // Inspection Plans
118
+ InspectionPlanSchema,
119
+ InspectionPlansListResponseSchema,
120
+ // Test Plans
121
+ TestPlanSchema,
122
+ TestPlansListResponseSchema,
123
+ // Forms
124
+ FormSchema,
125
+ FormsListResponseSchema,
126
+ FormResponseSchema,
127
+ // Tasks
128
+ TaskSchema,
129
+ TaskAttachmentSchema,
130
+ TaskChangeSchema,
131
+ TaskChangeActorSchema,
132
+ TaskChangeFieldsSchema,
133
+ TaskChangeLocationSchema,
134
+ TaskListParamsSchema,
135
+ TasksListResponseSchema,
136
+ TaskResponseSchema,
137
+ TaskChangeResponseSchema,
138
+ TaskChangesSchema,
139
+ TaskAttachmentsListResponseSchema,
140
+ // Project Templates (same ProjectTemplateSchema as Projects, re-exported above)
141
+ // File Revisions
142
+ FileRevisionSchema,
143
+ // File Upload
144
+ FileUploadSchema,
145
+ // Work Packages
146
+ WorkPackageSchema,
147
+ WorkPackagesListResponseSchema,
148
+ };
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema } = require('../helpers');
5
+
6
+ /**
7
+ * Mirrors models/inspection_plans/models.py::InspectionPlan — an empty
8
+ * placeholder in Python, so list items stay untyped (unwrapped but
9
+ * otherwise unvalidated).
10
+ */
11
+ const InspectionPlanSchema = z.any();
12
+
13
+ const InspectionPlansListResponseSchema = listResponseSchema(InspectionPlanSchema);
14
+
15
+ module.exports = {
16
+ InspectionPlanSchema,
17
+ InspectionPlansListResponseSchema,
18
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const { ProjectTemplateSchema } = require('../projects');
4
+
5
+ /**
6
+ * Mirrors models/project_templates/models.py::ProjectTemplate — identical
7
+ * shape to projects/models.py::ProjectTemplate, so it's reused rather than
8
+ * duplicated. listProjectTemplates returns raw/unmodeled data in Python too
9
+ * (company-wide endpoint), so no response wrapper is defined here.
10
+ */
11
+ module.exports = { ProjectTemplateSchema };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const { z } = require('zod');
4
+ const { listResponseSchema, singleResponseSchema } = require('../helpers');
5
+
6
+ /** Mirrors models/projects/models.py::ProjectModule (empty placeholder, passthrough). */
7
+ const ProjectModuleSchema = z.object({}).passthrough();
8
+
9
+ /** Mirrors models/projects/models.py::Project. Note: `type` is the real JSON key (Python aliases project_type -> "type"). */
10
+ const ProjectSchema = z.object({
11
+ projectId: z.string(),
12
+ projectName: z.string(),
13
+ type: z.string().nullish(),
14
+ projectTemplateId: z.string().nullish(),
15
+ address: z.string().nullish(),
16
+ number: z.string().nullish(),
17
+ created: z.string().nullish(),
18
+ closing: z.string().nullish(),
19
+ modules: z.array(ProjectModuleSchema).nullish(),
20
+ });
21
+
22
+ /** Mirrors models/projects/models.py::ProjectMetadata. */
23
+ const ProjectMetadataSchema = z.object({
24
+ key: z.string(),
25
+ value: z.string().nullish(),
26
+ });
27
+
28
+ /** Mirrors models/projects/models.py::ProjectTemplate (also duplicated in project_templates/models.py — same shape). */
29
+ const ProjectTemplateSchema = z.object({
30
+ projectTemplateId: z.string(),
31
+ name: z.string(),
32
+ });
33
+
34
+ /** Mirrors models/projects/models.py::ProjectCompany. */
35
+ const ProjectCompanySchema = z.object({
36
+ companyId: z.string().nullish(),
37
+ name: z.string().nullish(),
38
+ vatNumber: z.string().nullish(),
39
+ address: z.string().nullish(),
40
+ city: z.string().nullish(),
41
+ postalCode: z.string().nullish(),
42
+ country: z.string().nullish(),
43
+ catalogCompanyId: z.string().nullish(),
44
+ });
45
+
46
+ const ProjectsListResponseSchema = listResponseSchema(ProjectSchema);
47
+ const ProjectResponseSchema = singleResponseSchema(ProjectSchema);
48
+
49
+ module.exports = {
50
+ ProjectModuleSchema,
51
+ ProjectSchema,
52
+ ProjectMetadataSchema,
53
+ ProjectTemplateSchema,
54
+ ProjectCompanySchema,
55
+ ProjectsListResponseSchema,
56
+ ProjectResponseSchema,
57
+ };