@opra/core 0.3.0 → 0.5.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.
Files changed (58) hide show
  1. package/cjs/adapter/adapter.js +318 -0
  2. package/cjs/{implementation → adapter}/express-adapter.js +3 -6
  3. package/cjs/adapter/http-adapter.js +241 -0
  4. package/cjs/adapter/metadata-resource.js +23 -0
  5. package/cjs/{implementation → adapter}/query-context.js +3 -3
  6. package/cjs/index.js +6 -6
  7. package/cjs/interfaces/resource.interface.js +2 -0
  8. package/cjs/services/json-collection-service.js +14 -14
  9. package/cjs/services/json-singleton-service.js +97 -0
  10. package/esm/{implementation → adapter}/adapter.d.ts +17 -9
  11. package/esm/adapter/adapter.js +314 -0
  12. package/esm/{implementation → adapter}/express-adapter.d.ts +2 -2
  13. package/esm/{implementation → adapter}/express-adapter.js +3 -6
  14. package/esm/{implementation → adapter}/http-adapter.d.ts +2 -3
  15. package/esm/adapter/http-adapter.js +237 -0
  16. package/esm/adapter/metadata-resource.d.ts +8 -0
  17. package/esm/adapter/metadata-resource.js +20 -0
  18. package/esm/{implementation → adapter}/query-context.d.ts +6 -7
  19. package/esm/{implementation → adapter}/query-context.js +1 -1
  20. package/esm/index.d.ts +6 -6
  21. package/esm/index.js +6 -6
  22. package/esm/interfaces/resource.interface.d.ts +22 -0
  23. package/esm/interfaces/resource.interface.js +1 -0
  24. package/esm/services/json-collection-service.d.ts +11 -12
  25. package/esm/services/json-collection-service.js +15 -15
  26. package/esm/services/json-singleton-service.d.ts +39 -0
  27. package/esm/services/json-singleton-service.js +92 -0
  28. package/esm/types.d.ts +2 -8
  29. package/esm/utils/create-i18n.d.ts +1 -1
  30. package/package.json +15 -13
  31. package/cjs/enums/http-headers.enum.js +0 -395
  32. package/cjs/enums/http-status.enum.js +0 -300
  33. package/cjs/enums/index.js +0 -5
  34. package/cjs/implementation/adapter-utils/entity-resource-execute.util.js +0 -86
  35. package/cjs/implementation/adapter-utils/resource-execute.util.js +0 -11
  36. package/cjs/implementation/adapter-utils/resource-prepare.util.js +0 -11
  37. package/cjs/implementation/adapter.js +0 -130
  38. package/cjs/implementation/headers-map.js +0 -18
  39. package/cjs/implementation/http-adapter.js +0 -253
  40. package/cjs/interfaces/entity-service.interface.js +0 -30
  41. package/esm/enums/http-headers.enum.d.ts +0 -370
  42. package/esm/enums/http-headers.enum.js +0 -392
  43. package/esm/enums/http-status.enum.d.ts +0 -290
  44. package/esm/enums/http-status.enum.js +0 -297
  45. package/esm/enums/index.d.ts +0 -2
  46. package/esm/enums/index.js +0 -2
  47. package/esm/implementation/adapter-utils/entity-resource-execute.util.d.ts +0 -3
  48. package/esm/implementation/adapter-utils/entity-resource-execute.util.js +0 -82
  49. package/esm/implementation/adapter-utils/resource-execute.util.d.ts +0 -3
  50. package/esm/implementation/adapter-utils/resource-execute.util.js +0 -7
  51. package/esm/implementation/adapter-utils/resource-prepare.util.d.ts +0 -3
  52. package/esm/implementation/adapter-utils/resource-prepare.util.js +0 -7
  53. package/esm/implementation/adapter.js +0 -126
  54. package/esm/implementation/headers-map.d.ts +0 -5
  55. package/esm/implementation/headers-map.js +0 -14
  56. package/esm/implementation/http-adapter.js +0 -249
  57. package/esm/interfaces/entity-service.interface.d.ts +0 -19
  58. package/esm/interfaces/entity-service.interface.js +0 -26
@@ -0,0 +1,237 @@
1
+ import { HeadersMap, HttpHeaders, HttpStatus } from '@opra/common';
2
+ import { BadRequestError, InternalServerError, IssueSeverity, MethodNotAllowedError, NotFoundError, OpraException, wrapException } from '@opra/exception';
3
+ import { CollectionCreateQuery, CollectionDeleteManyQuery, CollectionDeleteQuery, CollectionGetQuery, CollectionResourceInfo, CollectionSearchQuery, CollectionUpdateManyQuery, CollectionUpdateQuery, ComplexType, ContainerResourceInfo, FieldGetQuery, OpraSchema, SingletonGetQuery, SingletonResourceInfo, UnionType, } from '@opra/schema';
4
+ import { OpraURL } from '@opra/url';
5
+ import { OpraAdapter } from './adapter.js';
6
+ import { QueryContext } from './query-context.js';
7
+ export class OpraHttpAdapter extends OpraAdapter {
8
+ prepareRequests(executionContext) {
9
+ const req = executionContext.getRequestWrapper();
10
+ // todo implement batch requests
11
+ if (this.isBatch(executionContext)) {
12
+ throw new Error('not implemented yet');
13
+ }
14
+ const url = new OpraURL(req.getUrl());
15
+ return [
16
+ this.prepareRequest(executionContext, url, req.getMethod(), new HeadersMap(req.getHeaders()), req.getBody())
17
+ ];
18
+ }
19
+ prepareRequest(executionContext, url, method, headers, body) {
20
+ if (!url.path.size)
21
+ throw new BadRequestError();
22
+ if (method !== 'GET' && url.path.size > 1)
23
+ throw new BadRequestError();
24
+ const query = this.buildQuery(url, method, body);
25
+ if (!query)
26
+ throw new MethodNotAllowedError({
27
+ message: `Method "${method}" is not allowed by target endpoint`
28
+ });
29
+ return new QueryContext({
30
+ service: this.document,
31
+ executionContext,
32
+ query,
33
+ headers: new HeadersMap(),
34
+ params: url.searchParams,
35
+ continueOnError: query.operation === 'read'
36
+ });
37
+ }
38
+ buildQuery(url, method, body) {
39
+ const pathLen = url.path.size;
40
+ let p = url.path.get(0);
41
+ let resource = this._internalResources.get(p.resource) || this.document.getResource(p.resource);
42
+ let container;
43
+ let pathIndex = 0;
44
+ while (resource && resource instanceof ContainerResourceInfo) {
45
+ container = resource;
46
+ p = url.path.get(++pathIndex);
47
+ resource = container.getResource(p.resource);
48
+ }
49
+ try {
50
+ method = method.toUpperCase();
51
+ let query;
52
+ if (resource instanceof SingletonResourceInfo && !p.key) {
53
+ switch (method) {
54
+ case 'GET': {
55
+ query = new SingletonGetQuery(resource);
56
+ }
57
+ }
58
+ }
59
+ else if (resource instanceof CollectionResourceInfo) {
60
+ switch (method) {
61
+ case 'GET': {
62
+ if (p.key) {
63
+ const searchParams = url.searchParams;
64
+ query = new CollectionGetQuery(resource, p.key, {
65
+ pick: searchParams.get('$pick'),
66
+ omit: searchParams.get('$omit'),
67
+ include: searchParams.get('$include')
68
+ });
69
+ }
70
+ else {
71
+ const searchParams = url.searchParams;
72
+ query = new CollectionSearchQuery(resource, {
73
+ filter: searchParams.get('$filter'),
74
+ limit: searchParams.get('$limit'),
75
+ skip: searchParams.get('$skip'),
76
+ distinct: searchParams.get('$distinct'),
77
+ count: searchParams.get('$count'),
78
+ sort: searchParams.get('$sort'),
79
+ pick: searchParams.get('$pick'),
80
+ omit: searchParams.get('$omit'),
81
+ include: searchParams.get('$include')
82
+ });
83
+ }
84
+ break;
85
+ }
86
+ case 'DELETE': {
87
+ const searchParams = url.searchParams;
88
+ query = p.key
89
+ ? new CollectionDeleteQuery(resource, p.key)
90
+ : new CollectionDeleteManyQuery(resource, {
91
+ filter: searchParams.get('$filter'),
92
+ });
93
+ break;
94
+ }
95
+ case 'POST': {
96
+ if (!p.key) {
97
+ const searchParams = url.searchParams;
98
+ query = new CollectionCreateQuery(resource, body, {
99
+ pick: searchParams.get('$pick'),
100
+ omit: searchParams.get('$omit'),
101
+ include: searchParams.get('$include')
102
+ });
103
+ }
104
+ break;
105
+ }
106
+ case 'PATCH': {
107
+ if (p.key) {
108
+ const searchParams = url.searchParams;
109
+ query = new CollectionUpdateQuery(resource, p.key, body, {
110
+ pick: searchParams.get('$pick'),
111
+ omit: searchParams.get('$omit'),
112
+ include: searchParams.get('$include')
113
+ });
114
+ }
115
+ else {
116
+ const searchParams = url.searchParams;
117
+ query = new CollectionUpdateManyQuery(resource, body, {
118
+ filter: searchParams.get('$filter')
119
+ });
120
+ }
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ else
126
+ throw new InternalServerError();
127
+ if (query instanceof SingletonGetQuery || query instanceof CollectionGetQuery || query instanceof FieldGetQuery) {
128
+ // Move through properties
129
+ let parentType;
130
+ const curPath = [];
131
+ let parent = query;
132
+ while (++pathIndex < pathLen) {
133
+ p = url.path.get(pathIndex);
134
+ parentType = parent.dataType;
135
+ if (parent.dataType instanceof UnionType) {
136
+ if (parent.dataType.name === 'any')
137
+ parentType = this.document.getComplexDataType('object');
138
+ else
139
+ throw new TypeError(`"${resource.name}.${curPath.join()}" is a UnionType and needs type casting.`);
140
+ }
141
+ if (!(parentType instanceof ComplexType))
142
+ throw new TypeError(`"${resource.name}.${curPath.join()}" is not a ComplexType and has no fields.`);
143
+ curPath.push(p.resource);
144
+ parent.child = new FieldGetQuery(parent, p.resource, { castingType: parentType });
145
+ parent = parent.child;
146
+ }
147
+ }
148
+ return query;
149
+ }
150
+ catch (e) {
151
+ if (e instanceof OpraException)
152
+ throw e;
153
+ throw new BadRequestError(e);
154
+ }
155
+ }
156
+ async sendResponse(executionContext, queryContexts) {
157
+ const outputPackets = [];
158
+ for (const ctx of queryContexts) {
159
+ const v = this.createOutput(ctx);
160
+ outputPackets.push(v);
161
+ }
162
+ if (this.isBatch(executionContext)) {
163
+ // this.writeError([], new InternalServerError({message: 'Not implemented yet'}));
164
+ return;
165
+ }
166
+ if (!outputPackets.length)
167
+ return this.sendError(executionContext, new NotFoundError());
168
+ const out = outputPackets[0];
169
+ const resp = executionContext.getResponseWrapper();
170
+ resp.setStatus(out.status);
171
+ resp.setHeader(HttpHeaders.Content_Type, 'application/json');
172
+ resp.setHeader(HttpHeaders.Cache_Control, 'no-cache');
173
+ resp.setHeader(HttpHeaders.Pragma, 'no-cache');
174
+ resp.setHeader(HttpHeaders.Expires, '-1');
175
+ resp.setHeader(HttpHeaders.X_Opra_Version, OpraSchema.Version);
176
+ if (out.headers) {
177
+ for (const [k, v] of Object.entries(out.headers)) {
178
+ resp.setHeader(k, v);
179
+ }
180
+ }
181
+ resp.send(JSON.stringify(out.body));
182
+ resp.end();
183
+ }
184
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
185
+ isBatch(executionContext) {
186
+ return false;
187
+ }
188
+ createOutput(ctx) {
189
+ const { query } = ctx;
190
+ let body;
191
+ let status = ctx.status || 0;
192
+ const errors = ctx.errors.map(e => wrapException(e));
193
+ if (errors && errors.length) {
194
+ // Sort errors from fatal to info
195
+ errors.sort((a, b) => {
196
+ const i = IssueSeverity.Keys.indexOf(a.issue.severity) - IssueSeverity.Keys.indexOf(b.issue.severity);
197
+ if (i === 0)
198
+ return b.status - a.status;
199
+ return i;
200
+ });
201
+ if (!status || status < HttpStatus.BAD_REQUEST) {
202
+ status = errors[0].status;
203
+ if (status < HttpStatus.BAD_REQUEST)
204
+ status = HttpStatus.INTERNAL_SERVER_ERROR;
205
+ }
206
+ body = {
207
+ operation: ctx.query.method,
208
+ errors: errors.map(e => e.issue)
209
+ };
210
+ }
211
+ else {
212
+ body = ctx.response;
213
+ status = status || (query.operation === 'create' ? HttpStatus.CREATED : HttpStatus.OK);
214
+ }
215
+ body = this.i18n.deep(body);
216
+ return {
217
+ status,
218
+ headers: ctx.responseHeaders.toObject(),
219
+ body
220
+ };
221
+ }
222
+ async sendError(executionContext, error) {
223
+ const resp = executionContext.getResponseWrapper();
224
+ resp.setStatus(error.status || 500);
225
+ resp.setHeader(HttpHeaders.Content_Type, 'application/json');
226
+ resp.setHeader(HttpHeaders.Cache_Control, 'no-cache');
227
+ resp.setHeader(HttpHeaders.Pragma, 'no-cache');
228
+ resp.setHeader(HttpHeaders.Expires, '-1');
229
+ resp.setHeader(HttpHeaders.X_Opra_Version, OpraSchema.Version);
230
+ const issue = this.i18n.deep(error.issue);
231
+ const body = {
232
+ operation: 'unknown',
233
+ errors: [issue]
234
+ };
235
+ resp.send(JSON.stringify(body));
236
+ }
237
+ }
@@ -0,0 +1,8 @@
1
+ import { SingletonResourceInfo } from '@opra/schema';
2
+ import { ISingletonResource } from '../interfaces/resource.interface.js';
3
+ import { JsonSingletonService } from '../services/json-singleton-service.js';
4
+ export declare class MetadataResource implements ISingletonResource<any> {
5
+ service: JsonSingletonService<any>;
6
+ init(resource: SingletonResourceInfo): void;
7
+ get(): any;
8
+ }
@@ -0,0 +1,20 @@
1
+ import { __decorate } from "tslib";
2
+ import { OprSingletonResource } from '@opra/schema';
3
+ import { JsonSingletonService } from '../services/json-singleton-service.js';
4
+ let MetadataResource = class MetadataResource {
5
+ service;
6
+ init(resource) {
7
+ this.service = new JsonSingletonService(resource.dataType, {
8
+ data: resource.document.getMetadata(true)
9
+ });
10
+ }
11
+ get() {
12
+ return this.service.get();
13
+ }
14
+ };
15
+ MetadataResource = __decorate([
16
+ OprSingletonResource(Object, {
17
+ name: '$metadata'
18
+ })
19
+ ], MetadataResource);
20
+ export { MetadataResource };
@@ -1,15 +1,14 @@
1
+ import { HeadersMap, HttpStatus } from '@opra/common';
1
2
  import { OpraException } from '@opra/exception';
2
- import { OpraAnyQuery, OpraApi } from '@opra/schema';
3
- import { SearchParams } from '@opra/url';
4
- import { HttpStatus } from '../enums/index.js';
3
+ import { OpraDocument, OpraQuery } from '@opra/schema';
4
+ import { OpraURLSearchParams } from '@opra/url';
5
5
  import { ContextType, IExecutionContext, IHttpExecutionContext } from '../interfaces/execution-context.interface.js';
6
- import { HeadersMap } from './headers-map.js';
7
6
  export declare type QueryContextArgs = Pick<QueryContext, 'service' | 'executionContext' | 'query' | 'params' | 'headers' | 'userContext' | 'parentValue' | 'continueOnError'>;
8
7
  export declare class QueryContext {
9
- readonly service: OpraApi;
8
+ readonly service: OpraDocument;
10
9
  readonly executionContext: IExecutionContext;
11
- readonly query: OpraAnyQuery;
12
- readonly params: SearchParams;
10
+ readonly query: OpraQuery;
11
+ readonly params: OpraURLSearchParams;
13
12
  readonly headers: HeadersMap;
14
13
  readonly parentValue?: any;
15
14
  readonly resultPath: string;
@@ -1,5 +1,5 @@
1
+ import { HeadersMap } from '@opra/common';
1
2
  import { OpraURLSearchParams } from '@opra/url';
2
- import { HeadersMap } from './headers-map.js';
3
3
  export class QueryContext {
4
4
  service;
5
5
  executionContext;
package/esm/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import "reflect-metadata";
2
2
  export * from './types.js';
3
- export * from './enums/index.js';
4
3
  export * from './interfaces/execution-context.interface.js';
5
- export * from './interfaces/entity-service.interface.js';
6
- export * from './implementation/query-context.js';
7
- export * from './implementation/adapter.js';
8
- export * from './implementation/http-adapter.js';
9
- export * from './implementation/express-adapter.js';
4
+ export * from './interfaces/resource.interface.js';
5
+ export * from './adapter/query-context.js';
6
+ export * from './adapter/adapter.js';
7
+ export * from './adapter/http-adapter.js';
8
+ export * from './adapter/express-adapter.js';
10
9
  export * from './services/data-service.js';
11
10
  export * from './services/json-collection-service.js';
11
+ export * from './services/json-singleton-service.js';
package/esm/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import "reflect-metadata";
2
2
  export * from './types.js';
3
- export * from './enums/index.js';
4
3
  export * from './interfaces/execution-context.interface.js';
5
- export * from './interfaces/entity-service.interface.js';
6
- export * from './implementation/query-context.js';
7
- export * from './implementation/adapter.js';
8
- export * from './implementation/http-adapter.js';
9
- export * from './implementation/express-adapter.js';
4
+ export * from './interfaces/resource.interface.js';
5
+ export * from './adapter/query-context.js';
6
+ export * from './adapter/adapter.js';
7
+ export * from './adapter/http-adapter.js';
8
+ export * from './adapter/express-adapter.js';
10
9
  export * from './services/data-service.js';
11
10
  export * from './services/json-collection-service.js';
11
+ export * from './services/json-singleton-service.js';
@@ -0,0 +1,22 @@
1
+ import { Maybe } from 'ts-gems';
2
+ import { ResourceInfo } from '@opra/schema';
3
+ import { PartialOutput } from '../types.js';
4
+ export interface IResource {
5
+ init?(resource: ResourceInfo): void | Promise<void>;
6
+ }
7
+ export interface ICollectionResource<T, TOutput = PartialOutput<T>> extends IResource {
8
+ create?(...args: any[]): TOutput | Promise<TOutput>;
9
+ count?(...args: any[]): number | Promise<number>;
10
+ delete?(...args: any[]): boolean | Promise<boolean>;
11
+ deleteMany?(...args: any[]): number | Promise<number>;
12
+ get?(...args: any[]): Maybe<TOutput> | Promise<Maybe<TOutput>>;
13
+ search?(...args: any[]): TOutput[] | Promise<TOutput[]>;
14
+ update?(...args: any[]): Maybe<TOutput> | Promise<Maybe<TOutput>>;
15
+ updateMany?(...args: any[]): number | Promise<number>;
16
+ }
17
+ export interface ISingletonResource<T, TOutput = PartialOutput<T>> extends IResource {
18
+ create?(...args: any[]): TOutput | Promise<TOutput>;
19
+ delete?(...args: any[]): boolean | Promise<boolean>;
20
+ get?(...args: any[]): Maybe<TOutput> | Promise<Maybe<TOutput>>;
21
+ update?(...args: any[]): Maybe<TOutput> | Promise<Maybe<TOutput>>;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,22 +1,21 @@
1
1
  import { Maybe } from 'ts-gems';
2
- import { ComplexType, EntityResource, OpraAnyQuery, OpraSchema } from '@opra/schema';
2
+ import { CollectionResourceInfo, ComplexType, OpraQuery, OpraSchema } from '@opra/schema';
3
3
  import { Expression } from '@opra/url';
4
- import { QueryContext } from '../implementation/query-context.js';
5
- import { IEntityService } from '../interfaces/entity-service.interface.js';
6
- import { EntityInput, EntityOutput } from '../types.js';
4
+ import { QueryContext } from '../adapter/query-context.js';
5
+ import { PartialInput, PartialOutput } from '../types.js';
7
6
  export interface JsonCollectionServiceOptions {
8
7
  resourceName?: string;
9
8
  defaultLimit?: number;
10
9
  data?: any[];
11
10
  }
12
- export declare class JsonCollectionService<T, TOutput = EntityOutput<T>> implements IEntityService {
13
- readonly resource: EntityResource;
11
+ export declare class JsonCollectionService<T, TOutput = PartialOutput<T>> {
12
+ readonly resource: CollectionResourceInfo;
14
13
  private _status;
15
14
  private _initError;
16
15
  private _dbName;
17
16
  private _initData?;
18
17
  defaultLimit: number;
19
- constructor(resource: EntityResource, options?: JsonCollectionServiceOptions);
18
+ constructor(resource: CollectionResourceInfo, options?: JsonCollectionServiceOptions);
20
19
  get dataType(): ComplexType;
21
20
  get primaryKey(): string;
22
21
  get resourceName(): string;
@@ -25,15 +24,15 @@ export declare class JsonCollectionService<T, TOutput = EntityOutput<T>> impleme
25
24
  get(keyValue: any, options?: JsonCollectionService.GetOptions): Promise<Maybe<TOutput>>;
26
25
  count(options?: JsonCollectionService.SearchOptions): Promise<number>;
27
26
  search(options?: JsonCollectionService.SearchOptions): Promise<TOutput[]>;
28
- create(data: EntityInput<T>, options?: JsonCollectionService.CreateOptions): Promise<TOutput>;
29
- update(keyValue: any, data: EntityInput<T>, options?: JsonCollectionService.UpdateOptions): Promise<Maybe<TOutput>>;
30
- updateMany(data: EntityInput<T>, options?: JsonCollectionService.UpdateManyOptions): Promise<number>;
27
+ create(data: PartialInput<T>, options?: JsonCollectionService.CreateOptions): Promise<TOutput>;
28
+ update(keyValue: any, data: PartialInput<T>, options?: JsonCollectionService.UpdateOptions): Promise<Maybe<TOutput>>;
29
+ updateMany(data: PartialInput<T>, options?: JsonCollectionService.UpdateManyOptions): Promise<number>;
31
30
  delete(keyValue: any): Promise<boolean>;
32
31
  deleteMany(options?: JsonCollectionService.DeleteManyOptions): Promise<number>;
33
32
  private _waitInitializing;
34
33
  protected _init(): Promise<void>;
35
- protected _prepare(query: OpraAnyQuery): {
36
- method: OpraSchema.EntityMethod;
34
+ protected _prepare(query: OpraQuery): {
35
+ method: OpraSchema.CollectionMethod;
37
36
  options: any;
38
37
  keyValue?: any;
39
38
  values?: any;
@@ -2,7 +2,7 @@ import _ from 'lodash';
2
2
  import merge from 'putil-merge';
3
3
  import { nSQL } from "@nano-sql/core";
4
4
  import { BadRequestError, MethodNotAllowedError, ResourceConflictError } from '@opra/exception';
5
- import { ComplexType, EntityResource } from '@opra/schema';
5
+ import { CollectionResourceInfo, ComplexType } from '@opra/schema';
6
6
  import { $parse, ArrayExpression, BooleanLiteral, ComparisonExpression, DateLiteral, Expression, LogicalExpression, NullLiteral, NumberLiteral, ParenthesesExpression, QualifiedIdentifier, StringLiteral, TimeLiteral } from '@opra/url';
7
7
  import { pathToTree } from '../utils/path-to-tree.js';
8
8
  let dbId = 1;
@@ -191,7 +191,7 @@ export class JsonCollectionService {
191
191
  indexes: {}
192
192
  };
193
193
  for (const [k, f] of this.resource.dataType.fields.entries()) {
194
- const fieldType = this.resource.owner.getDataType(f.type || 'string');
194
+ const fieldType = this.resource.document.getDataType(f.type || 'string');
195
195
  const o = table.model[k + ':' + dataTypeToSQLType(fieldType, !!f.isArray)] = {};
196
196
  if (k === this.primaryKey)
197
197
  o.pk = true;
@@ -199,21 +199,21 @@ export class JsonCollectionService {
199
199
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
200
200
  const indexes = table.indexes;
201
201
  // Add indexes for sort fields
202
- const searchMethod = this.resource.metadata.methods.search;
203
- if (searchMethod) {
204
- if (searchMethod.sortFields) {
205
- searchMethod.sortFields.forEach(fieldName => {
202
+ const searchResolver = this.resource.metadata.search;
203
+ if (searchResolver) {
204
+ if (searchResolver.sortFields) {
205
+ searchResolver.sortFields.forEach(fieldName => {
206
206
  const f = this.dataType.getField(fieldName);
207
- const fieldType = this.resource.owner.getDataType(f.type || 'string');
207
+ const fieldType = this.resource.document.getDataType(f.type || 'string');
208
208
  const t = dataTypeToSQLType(fieldType, !!f.isArray);
209
209
  if (indexingTypes.includes(t))
210
210
  indexes[fieldName + ':' + t] = {};
211
211
  });
212
212
  }
213
- if (searchMethod.filters) {
214
- searchMethod.filters.forEach(filter => {
213
+ if (searchResolver.filters) {
214
+ searchResolver.filters.forEach(filter => {
215
215
  const f = this.dataType.getField(filter.field);
216
- const fieldType = this.resource.owner.getDataType(f.type || 'string');
216
+ const fieldType = this.resource.document.getDataType(f.type || 'string');
217
217
  const t = dataTypeToSQLType(fieldType, !!f.isArray);
218
218
  if (indexingTypes.includes(t))
219
219
  indexes[filter.field + ':' + t] = {};
@@ -241,10 +241,10 @@ export class JsonCollectionService {
241
241
  }
242
242
  }
243
243
  _prepare(query) {
244
- if (query.resource instanceof EntityResource) {
244
+ if (query.resource instanceof CollectionResourceInfo) {
245
245
  if (query.dataType !== this.dataType)
246
246
  throw new TypeError(`Query data type (${query.dataType.name}) ` +
247
- `differs from JsonDataService data type (${this.dataType.name})`);
247
+ `differs from JsonCollectionService data type (${this.dataType.name})`);
248
248
  }
249
249
  switch (query.method) {
250
250
  case 'count': {
@@ -272,7 +272,7 @@ export class JsonCollectionService {
272
272
  };
273
273
  }
274
274
  case 'get': {
275
- if (query.kind === 'GetInstanceQuery') {
275
+ if (query.kind === 'CollectionGetQuery') {
276
276
  const options = _.omitBy({
277
277
  pick: query.pick,
278
278
  omit: query.omit,
@@ -286,7 +286,7 @@ export class JsonCollectionService {
286
286
  args: [keyValue, options]
287
287
  };
288
288
  }
289
- if (query.kind === 'GetFieldQuery') {
289
+ if (query.kind === 'FieldGetQuery') {
290
290
  // todo
291
291
  }
292
292
  break;
@@ -365,7 +365,7 @@ export class JsonCollectionService {
365
365
  }
366
366
  _convertSelect(args) {
367
367
  const result = [];
368
- const document = this.dataType.owner;
368
+ const document = this.dataType.document;
369
369
  const processDataType = (dt, path, pick, omit, include) => {
370
370
  let kl;
371
371
  for (const [k, f] of dt.fields) {
@@ -0,0 +1,39 @@
1
+ import { DataType, OpraQuery, OpraSchema } from '@opra/schema';
2
+ import { QueryContext } from '../adapter/query-context.js';
3
+ import { PartialOutput } from '../types.js';
4
+ export interface JsonSingletonServiceOptions<T> {
5
+ data: T;
6
+ resourceName?: string;
7
+ }
8
+ export declare class JsonSingletonService<T, TOutput = PartialOutput<T>> {
9
+ readonly dataType: DataType;
10
+ protected _data?: T;
11
+ constructor(dataType: DataType, options?: JsonSingletonServiceOptions<T>);
12
+ processRequest(ctx: QueryContext): Promise<any>;
13
+ get(options?: JsonSingletonService.GetOptions): TOutput | undefined;
14
+ protected _prepare(query: OpraQuery): {
15
+ method: OpraSchema.SingletonMethod;
16
+ options: any;
17
+ keyValue?: any;
18
+ values?: any;
19
+ args: any[];
20
+ };
21
+ }
22
+ export declare namespace JsonSingletonService {
23
+ type CreateOptions = {
24
+ query?: string;
25
+ pick?: string[];
26
+ omit?: string[];
27
+ include?: string[];
28
+ };
29
+ type GetOptions = {
30
+ pick?: string[];
31
+ omit?: string[];
32
+ include?: string[];
33
+ };
34
+ type UpdateOptions = {
35
+ pick?: string[];
36
+ omit?: string[];
37
+ include?: string[];
38
+ };
39
+ }
@@ -0,0 +1,92 @@
1
+ import _ from 'lodash';
2
+ import { SingletonResourceInfo } from '@opra/schema';
3
+ export class JsonSingletonService {
4
+ dataType;
5
+ _data;
6
+ constructor(dataType, options) {
7
+ this.dataType = dataType;
8
+ this._data = options?.data;
9
+ }
10
+ async processRequest(ctx) {
11
+ const prepared = this._prepare(ctx.query);
12
+ const fn = this[prepared.method];
13
+ if (!fn)
14
+ throw new TypeError(`Unimplemented method (${prepared.method})`);
15
+ // @ts-ignore
16
+ return fn.apply(this, prepared.args);
17
+ }
18
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
19
+ get(options) {
20
+ return this._data;
21
+ }
22
+ _prepare(query) {
23
+ if (query.resource instanceof SingletonResourceInfo) {
24
+ if (query.dataType !== this.dataType)
25
+ throw new TypeError(`Query data type (${query.dataType.name}) ` +
26
+ `differs from JsonCollectionService data type (${this.dataType.name})`);
27
+ }
28
+ switch (query.method) {
29
+ case 'create': {
30
+ const options = _.omitBy({
31
+ pick: query.pick,
32
+ omit: query.omit,
33
+ include: query.include
34
+ }, _.isNil);
35
+ const { data } = query;
36
+ return {
37
+ method: query.method,
38
+ values: data,
39
+ options,
40
+ args: [data, options]
41
+ };
42
+ }
43
+ case 'get': {
44
+ if (query.kind === 'CollectionGetQuery') {
45
+ const options = _.omitBy({
46
+ pick: query.pick,
47
+ omit: query.omit,
48
+ include: query.include
49
+ }, _.isNil);
50
+ const keyValue = query.keyValue;
51
+ return {
52
+ method: query.method,
53
+ keyValue,
54
+ options,
55
+ args: [keyValue, options]
56
+ };
57
+ }
58
+ if (query.kind === 'FieldGetQuery') {
59
+ // todo
60
+ }
61
+ break;
62
+ }
63
+ case 'update': {
64
+ const options = _.omitBy({
65
+ pick: query.pick,
66
+ omit: query.omit,
67
+ include: query.include
68
+ }, _.isNil);
69
+ const { data } = query;
70
+ const keyValue = query.keyValue;
71
+ return {
72
+ method: query.method,
73
+ keyValue: query.keyValue,
74
+ values: data,
75
+ options,
76
+ args: [keyValue, data, options]
77
+ };
78
+ }
79
+ case 'delete': {
80
+ const options = {};
81
+ const keyValue = query.keyValue;
82
+ return {
83
+ method: query.method,
84
+ keyValue,
85
+ options,
86
+ args: [keyValue, options]
87
+ };
88
+ }
89
+ }
90
+ throw new Error(`Unimplemented query type "${query.method}"`);
91
+ }
92
+ }
package/esm/types.d.ts CHANGED
@@ -1,8 +1,2 @@
1
- import { Builtin, DeepPickWritable } from 'ts-gems';
2
- export declare type EntityInput<T> = DeepNullableIfPartial<DeepPickWritable<T>>;
3
- export declare type EntityOutput<T> = DeepNullableIfPartial<T>;
4
- export declare type DeepNullableIfPartial<T> = _DeepNullableIfPartial<T>;
5
- declare type _DeepNullableIfPartial<T> = T extends Builtin ? T : T extends Promise<infer U> ? Promise<DeepNullableIfPartial<U>> : T extends (infer U)[] ? DeepNullableIfPartial<U>[] : {
6
- [P in keyof T]?: DeepNullableIfPartial<Exclude<T[P], undefined>> | null;
7
- };
8
- export {};
1
+ import { PartialInput, PartialOutput } from '@opra/common';
2
+ export { PartialInput, PartialOutput };
@@ -1,3 +1,3 @@
1
1
  import { I18n } from '@opra/i18n';
2
- import { OpraAdapter } from '../implementation/adapter.js';
2
+ import { OpraAdapter } from '../adapter/adapter.js';
3
3
  export declare function createI18n(options?: OpraAdapter.I18nOptions): Promise<I18n>;