@flink-app/flink 0.11.4 → 0.11.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/FlinkApp.d.ts +5 -0
- package/dist/src/FlinkApp.js +25 -18
- package/dist/src/FlinkRepo.d.ts +1 -0
- package/dist/src/FlinkRepo.js +16 -3
- package/package.json +2 -2
- package/spec/FlinkRepo.spec.ts +45 -38
- package/src/FlinkApp.ts +14 -0
- package/src/FlinkRepo.ts +76 -62
package/dist/src/FlinkApp.d.ts
CHANGED
|
@@ -100,6 +100,10 @@ export interface FlinkOptions {
|
|
|
100
100
|
* Options for json body parser
|
|
101
101
|
*/
|
|
102
102
|
jsonOptions?: OptionsJson;
|
|
103
|
+
/**
|
|
104
|
+
* Optional: content-types that should be parsed as raw body, and passed as a Buffer object instead of json
|
|
105
|
+
*/
|
|
106
|
+
rawContentTypes?: string | string[];
|
|
103
107
|
scheduling?: {
|
|
104
108
|
/**
|
|
105
109
|
* If true, the scheduler will be enabled.
|
|
@@ -164,6 +168,7 @@ export declare class FlinkApp<C extends FlinkContext> {
|
|
|
164
168
|
private corsOpts;
|
|
165
169
|
private routingConfigured;
|
|
166
170
|
private jsonOptions?;
|
|
171
|
+
private rawContentTypes?;
|
|
167
172
|
private schedulingOptions?;
|
|
168
173
|
private disableHttpServer;
|
|
169
174
|
private repos;
|
package/dist/src/FlinkApp.js
CHANGED
|
@@ -107,6 +107,7 @@ var FlinkApp = /** @class */ (function () {
|
|
|
107
107
|
this.onDbConnection = opts.onDbConnection;
|
|
108
108
|
this.plugins = opts.plugins || [];
|
|
109
109
|
this.corsOpts = __assign(__assign({}, defaultCorsOptions), opts.cors);
|
|
110
|
+
this.rawContentTypes = Array.isArray(opts.rawContentTypes) ? opts.rawContentTypes : (typeof opts.rawContentTypes === 'string' ? [opts.rawContentTypes] : undefined);
|
|
110
111
|
this.auth = opts.auth;
|
|
111
112
|
this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
|
|
112
113
|
this.schedulingOptions = opts.scheduling;
|
|
@@ -126,23 +127,23 @@ var FlinkApp = /** @class */ (function () {
|
|
|
126
127
|
FlinkApp.prototype.start = function () {
|
|
127
128
|
var _a;
|
|
128
129
|
return __awaiter(this, void 0, void 0, function () {
|
|
129
|
-
var startTime, offsetTime, _i, _b, plugin, db;
|
|
130
|
+
var startTime, offsetTime, _i, _b, type, _c, _d, plugin, db;
|
|
130
131
|
var _this = this;
|
|
131
|
-
return __generator(this, function (
|
|
132
|
-
switch (
|
|
132
|
+
return __generator(this, function (_e) {
|
|
133
|
+
switch (_e.label) {
|
|
133
134
|
case 0:
|
|
134
135
|
startTime = Date.now();
|
|
135
136
|
offsetTime = 0;
|
|
136
137
|
return [4 /*yield*/, this.initDb()];
|
|
137
138
|
case 1:
|
|
138
|
-
|
|
139
|
+
_e.sent();
|
|
139
140
|
if (this.debug) {
|
|
140
141
|
offsetTime = Date.now();
|
|
141
142
|
FlinkLog_1.log.bgColorLog("cyan", "Init db took " + (offsetTime - startTime) + " ms");
|
|
142
143
|
}
|
|
143
144
|
return [4 /*yield*/, this.buildContext()];
|
|
144
145
|
case 2:
|
|
145
|
-
|
|
146
|
+
_e.sent();
|
|
146
147
|
if (this.debug) {
|
|
147
148
|
FlinkLog_1.log.bgColorLog("cyan", "Build context took " + (Date.now() - offsetTime) + " ms");
|
|
148
149
|
offsetTime = Date.now();
|
|
@@ -156,6 +157,12 @@ var FlinkApp = /** @class */ (function () {
|
|
|
156
157
|
if (!this.disableHttpServer) {
|
|
157
158
|
this.expressApp = express_1.default();
|
|
158
159
|
this.expressApp.use(cors_1.default(this.corsOpts));
|
|
160
|
+
if (this.rawContentTypes) {
|
|
161
|
+
for (_i = 0, _b = this.rawContentTypes; _i < _b.length; _i++) {
|
|
162
|
+
type = _b[_i];
|
|
163
|
+
this.expressApp.use(express_1.default.raw({ type: type }));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
159
166
|
this.expressApp.use(body_parser_1.default.json(this.jsonOptions));
|
|
160
167
|
if (this.accessLog.enabled) {
|
|
161
168
|
this.expressApp.use(morgan_1.default(this.accessLog.format));
|
|
@@ -165,32 +172,32 @@ var FlinkApp = /** @class */ (function () {
|
|
|
165
172
|
next();
|
|
166
173
|
});
|
|
167
174
|
}
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
_c = 0, _d = this.plugins;
|
|
176
|
+
_e.label = 3;
|
|
170
177
|
case 3:
|
|
171
|
-
if (!(
|
|
172
|
-
plugin =
|
|
178
|
+
if (!(_c < _d.length)) return [3 /*break*/, 9];
|
|
179
|
+
plugin = _d[_c];
|
|
173
180
|
db = void 0;
|
|
174
181
|
if (!plugin.db) return [3 /*break*/, 5];
|
|
175
182
|
return [4 /*yield*/, this.initPluginDb(plugin)];
|
|
176
183
|
case 4:
|
|
177
|
-
db =
|
|
178
|
-
|
|
184
|
+
db = _e.sent();
|
|
185
|
+
_e.label = 5;
|
|
179
186
|
case 5:
|
|
180
187
|
if (!plugin.init) return [3 /*break*/, 7];
|
|
181
188
|
return [4 /*yield*/, plugin.init(this, db)];
|
|
182
189
|
case 6:
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
_e.sent();
|
|
191
|
+
_e.label = 7;
|
|
185
192
|
case 7:
|
|
186
193
|
FlinkLog_1.log.info("Initialized plugin '" + plugin.id + "'");
|
|
187
|
-
|
|
194
|
+
_e.label = 8;
|
|
188
195
|
case 8:
|
|
189
|
-
|
|
196
|
+
_c++;
|
|
190
197
|
return [3 /*break*/, 3];
|
|
191
198
|
case 9: return [4 /*yield*/, this.registerAutoRegisterableHandlers()];
|
|
192
199
|
case 10:
|
|
193
|
-
|
|
200
|
+
_e.sent();
|
|
194
201
|
if (this.debug) {
|
|
195
202
|
FlinkLog_1.log.bgColorLog("cyan", "Register handlers took " + (Date.now() - offsetTime) + " ms");
|
|
196
203
|
offsetTime = Date.now();
|
|
@@ -198,12 +205,12 @@ var FlinkApp = /** @class */ (function () {
|
|
|
198
205
|
if (!this.isSchedulingEnabled) return [3 /*break*/, 12];
|
|
199
206
|
return [4 /*yield*/, this.registerAutoRegisterableJobs()];
|
|
200
207
|
case 11:
|
|
201
|
-
|
|
208
|
+
_e.sent();
|
|
202
209
|
if (this.debug) {
|
|
203
210
|
FlinkLog_1.log.bgColorLog("cyan", "Register jobs took " + (Date.now() - offsetTime) + " ms");
|
|
204
211
|
offsetTime = Date.now();
|
|
205
212
|
}
|
|
206
|
-
|
|
213
|
+
_e.label = 12;
|
|
207
214
|
case 12:
|
|
208
215
|
// Register 404 with slight delay to allow all manually added routes to be added
|
|
209
216
|
// TODO: Is there a better solution to force this handler to always run last?
|
package/dist/src/FlinkRepo.d.ts
CHANGED
|
@@ -17,4 +17,5 @@ export declare abstract class FlinkRepo<C extends FlinkContext, Model = any> {
|
|
|
17
17
|
updateOne<U = Partial<Model>>(id: string, model: U): Promise<Model>;
|
|
18
18
|
updateMany<U = Partial<Model>>(query: any, model: U): Promise<number>;
|
|
19
19
|
deleteById(id: string): Promise<number>;
|
|
20
|
+
private buildId;
|
|
20
21
|
}
|
package/dist/src/FlinkRepo.js
CHANGED
|
@@ -67,7 +67,7 @@ var FlinkRepo = /** @class */ (function () {
|
|
|
67
67
|
FlinkRepo.prototype.getById = function (id) {
|
|
68
68
|
return __awaiter(this, void 0, void 0, function () {
|
|
69
69
|
return __generator(this, function (_a) {
|
|
70
|
-
return [2 /*return*/, this.collection.findOne({ _id:
|
|
70
|
+
return [2 /*return*/, this.collection.findOne({ _id: this.buildId(id) })];
|
|
71
71
|
});
|
|
72
72
|
});
|
|
73
73
|
};
|
|
@@ -98,7 +98,7 @@ var FlinkRepo = /** @class */ (function () {
|
|
|
98
98
|
return __generator(this, function (_a) {
|
|
99
99
|
switch (_a.label) {
|
|
100
100
|
case 0:
|
|
101
|
-
oid =
|
|
101
|
+
oid = this.buildId(id);
|
|
102
102
|
return [4 /*yield*/, this.collection.updateOne({
|
|
103
103
|
_id: oid,
|
|
104
104
|
}, {
|
|
@@ -132,7 +132,7 @@ var FlinkRepo = /** @class */ (function () {
|
|
|
132
132
|
return __generator(this, function (_a) {
|
|
133
133
|
switch (_a.label) {
|
|
134
134
|
case 0: return [4 /*yield*/, this.collection.deleteOne({
|
|
135
|
-
_id:
|
|
135
|
+
_id: this.buildId(id),
|
|
136
136
|
})];
|
|
137
137
|
case 1:
|
|
138
138
|
deletedCount = (_a.sent()).deletedCount;
|
|
@@ -141,6 +141,19 @@ var FlinkRepo = /** @class */ (function () {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
};
|
|
144
|
+
FlinkRepo.prototype.buildId = function (id) {
|
|
145
|
+
var oid;
|
|
146
|
+
if (typeof id === "string") {
|
|
147
|
+
oid = new mongodb_1.ObjectId(id);
|
|
148
|
+
}
|
|
149
|
+
else if (id instanceof mongodb_1.ObjectId) {
|
|
150
|
+
oid = id;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
throw new Error("Invalid id type");
|
|
154
|
+
}
|
|
155
|
+
return oid;
|
|
156
|
+
};
|
|
144
157
|
return FlinkRepo;
|
|
145
158
|
}());
|
|
146
159
|
exports.FlinkRepo = FlinkRepo;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/flink",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.9",
|
|
4
4
|
"description": "Typescript only framework for creating REST-like APIs on top of Express and mongodb",
|
|
5
5
|
"types": "dist/src/index.d.ts",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"rimraf": "^3.0.2",
|
|
68
68
|
"ts-node": "^9.1.1"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "cc04e10ae82ed022031ef8bcfaafa9beb1d6564f"
|
|
71
71
|
}
|
package/spec/FlinkRepo.spec.ts
CHANGED
|
@@ -2,61 +2,68 @@ import { FlinkRepo } from "../src/FlinkRepo";
|
|
|
2
2
|
import mongodb, { Collection, Db } from "mongodb";
|
|
3
3
|
|
|
4
4
|
interface Model {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
_id: string;
|
|
6
|
+
name: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
class Repo extends FlinkRepo<any, Model> {}
|
|
10
10
|
|
|
11
11
|
describe("FlinkRepo", () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
if (!process.env.CI) {
|
|
13
|
+
console.warn("Skipping repo test which requires db if CI flag is not set");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
let db: Db;
|
|
18
|
+
let collection: Collection;
|
|
19
|
+
let repo: Repo;
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
db = client.db();
|
|
26
|
-
collection = db.collection("test-coll");
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
const client = await mongodb.connect("mongodb://localhost:27017/flink-test-db");
|
|
23
|
+
db = client.db();
|
|
24
|
+
collection = db.collection("test-coll");
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
repo = new Repo("test-coll", db);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should get document by id", async () => {
|
|
30
|
+
const { insertedId } = await collection.insertOne({ name: "foo" });
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
const { insertedId } = await collection.insertOne({ name: "foo" });
|
|
32
|
+
const doc = await repo.getById(insertedId + "");
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
expect(doc).toBeDefined();
|
|
35
|
+
expect(doc?.name).toBe("foo");
|
|
36
|
+
});
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
});
|
|
38
|
+
it("should get document by id using ObjectId", async () => {
|
|
39
|
+
const { insertedId } = await collection.insertOne({ name: "foo" });
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
const createdDoc = await repo.create<{ name: string }>({ name: "bar" });
|
|
41
|
+
const doc = await repo.getById(insertedId);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
expect(doc).toBeDefined();
|
|
44
|
+
expect(doc?.name).toBe("foo");
|
|
45
|
+
});
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
it("should create and delete document", async () => {
|
|
48
|
+
const createdDoc = await repo.create<{ name: string }>({ name: "bar" });
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
expect(createdDoc).toBeDefined();
|
|
51
|
+
expect(createdDoc?._id).toBeDefined();
|
|
52
|
+
expect(createdDoc?.name).toBe("bar");
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
const createdDoc = await repo.create({ name: "bar" });
|
|
54
|
+
const delCount = await repo.deleteById(createdDoc._id);
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
name: "foo",
|
|
56
|
+
expect(delCount).toBe(1);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
it("should update document", async () => {
|
|
60
|
+
const createdDoc = await repo.create({ name: "bar" });
|
|
61
|
+
|
|
62
|
+
const updatedDoc = await repo.updateOne(createdDoc._id + "", {
|
|
63
|
+
name: "foo",
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(updatedDoc).toBeDefined();
|
|
67
|
+
expect(updatedDoc.name).toBe("foo");
|
|
68
|
+
});
|
|
62
69
|
});
|
package/src/FlinkApp.ts
CHANGED
|
@@ -138,6 +138,11 @@ export interface FlinkOptions {
|
|
|
138
138
|
*/
|
|
139
139
|
jsonOptions?: OptionsJson;
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Optional: content-types that should be parsed as raw body, and passed as a Buffer object instead of json
|
|
143
|
+
*/
|
|
144
|
+
rawContentTypes?: string | string[];
|
|
145
|
+
|
|
141
146
|
scheduling?: {
|
|
142
147
|
/**
|
|
143
148
|
* If true, the scheduler will be enabled.
|
|
@@ -226,6 +231,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
226
231
|
private corsOpts: FlinkOptions["cors"];
|
|
227
232
|
private routingConfigured = false;
|
|
228
233
|
private jsonOptions?: OptionsJson;
|
|
234
|
+
private rawContentTypes?: string[];
|
|
229
235
|
private schedulingOptions?: FlinkOptions["scheduling"];
|
|
230
236
|
private disableHttpServer = false;
|
|
231
237
|
|
|
@@ -248,6 +254,7 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
248
254
|
this.onDbConnection = opts.onDbConnection;
|
|
249
255
|
this.plugins = opts.plugins || [];
|
|
250
256
|
this.corsOpts = { ...defaultCorsOptions, ...opts.cors };
|
|
257
|
+
this.rawContentTypes = Array.isArray(opts.rawContentTypes) ? opts.rawContentTypes : (typeof opts.rawContentTypes === 'string' ? [opts.rawContentTypes] : undefined);
|
|
251
258
|
this.auth = opts.auth;
|
|
252
259
|
this.jsonOptions = opts.jsonOptions || { limit: "1mb" };
|
|
253
260
|
this.schedulingOptions = opts.scheduling;
|
|
@@ -289,6 +296,13 @@ export class FlinkApp<C extends FlinkContext> {
|
|
|
289
296
|
if (!this.disableHttpServer) {
|
|
290
297
|
this.expressApp = express();
|
|
291
298
|
this.expressApp.use(cors(this.corsOpts));
|
|
299
|
+
|
|
300
|
+
if (this.rawContentTypes) {
|
|
301
|
+
for (const type of this.rawContentTypes) {
|
|
302
|
+
this.expressApp.use(express.raw({ type }));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
292
306
|
this.expressApp.use(bodyParser.json(this.jsonOptions));
|
|
293
307
|
|
|
294
308
|
if (this.accessLog.enabled) {
|
package/src/FlinkRepo.ts
CHANGED
|
@@ -1,66 +1,80 @@
|
|
|
1
|
-
import { Collection, Db,
|
|
1
|
+
import { Collection, Db, ObjectId } from "mongodb";
|
|
2
2
|
import { FlinkContext } from "./FlinkContext";
|
|
3
3
|
|
|
4
4
|
export abstract class FlinkRepo<C extends FlinkContext, Model = any> {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
5
|
+
collection: Collection;
|
|
6
|
+
|
|
7
|
+
private _ctx?: C;
|
|
8
|
+
|
|
9
|
+
set ctx(ctx: FlinkContext) {
|
|
10
|
+
this._ctx = ctx as C;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get ctx() {
|
|
14
|
+
if (!this._ctx) throw new Error("Missing FlinkContext");
|
|
15
|
+
return this._ctx;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(private collectionName: string, private db: Db) {
|
|
19
|
+
this.collection = db.collection(this.collectionName);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async findAll(query = {}): Promise<Model[]> {
|
|
23
|
+
return this.collection.find(query).toArray();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getById(id: string): Promise<Model | null> {
|
|
27
|
+
return this.collection.findOne({ _id: this.buildId(id) });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getOne(query = {}): Promise<Model | null> {
|
|
31
|
+
return this.collection.findOne(query);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async create<C = Omit<Model, "_id">>(model: C): Promise<C & { _id: string }> {
|
|
35
|
+
const { ops } = await this.collection.insertOne(model);
|
|
36
|
+
return ops[0];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async updateOne<U = Partial<Model>>(id: string, model: U): Promise<Model> {
|
|
40
|
+
const oid = this.buildId(id);
|
|
41
|
+
|
|
42
|
+
await this.collection.updateOne(
|
|
43
|
+
{
|
|
44
|
+
_id: oid,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
$set: model,
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
return this.collection.findOne({ _id: oid });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async updateMany<U = Partial<Model>>(query: any, model: U): Promise<number> {
|
|
54
|
+
const { modifiedCount } = await this.collection.updateMany(query, {
|
|
55
|
+
$set: model,
|
|
56
|
+
});
|
|
57
|
+
return modifiedCount;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async deleteById(id: string): Promise<number> {
|
|
61
|
+
const { deletedCount } = await this.collection.deleteOne({
|
|
62
|
+
_id: this.buildId(id),
|
|
63
|
+
});
|
|
64
|
+
return deletedCount || 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private buildId(id: string | ObjectId) {
|
|
68
|
+
let oid: ObjectId | string;
|
|
69
|
+
|
|
70
|
+
if (typeof id === "string") {
|
|
71
|
+
oid = new ObjectId(id);
|
|
72
|
+
} else if (id instanceof ObjectId) {
|
|
73
|
+
oid = id;
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error("Invalid id type");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return oid;
|
|
79
|
+
}
|
|
66
80
|
}
|