Skip to content

Commit bf5f92f

Browse files
stanleyphukkajla12
andauthored
Add v2 api support (#45)
* Update all endpoints to v2 * Create object types and module * Update modules to support v2 endpoints Main changes to support v2 compatibility: - Resource modules now have a meta field that can store any type of attribute rather than specific fields (name, description, etc) - List endpoints now consist of an array of results with an optional prevCursor or nextCursor used for pagination * Update /v2/authorize endpoint to /v2/check * Add tests for batch endpoints * Clean up unused imports * Export object and warrant modules * Include request options in query calls * Create ListResponse interface for list return type * Pass request options in Tenant.create * Add warrant query test * Add query filter to list objects filters * Return warrant token when creating/deleting warrants * Refactor parseResponse to be clearer * Return warrantToken for object deletes * Remove return awaits outside try-catch block * Update object methods to also accept WarrantObjects * Update tests to support typescript * Add params types for all methods where applicable * Improve usage of get and update methods * Add list warrants method --------- Co-authored-by: Karan Kajla <kkajla12@gmail.com>
1 parent fbd2bc6 commit bf5f92f

31 files changed

+2450
-1323
lines changed

package-lock.json

Lines changed: 474 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"build": "tsc",
1313
"build-local": "npm run build && npm pack",
1414
"prepare": "npm run build",
15-
"test": "npm run build && mocha"
15+
"test": "npm run build && mocha --require ts-node/register test/**/*.spec.ts"
1616
},
1717
"repository": {
1818
"type": "git",
@@ -35,8 +35,12 @@
3535
"node": ">=18.0.0"
3636
},
3737
"devDependencies": {
38+
"@types/chai": "^4.3.9",
39+
"@types/mocha": "^10.0.3",
3840
"@types/node": "^18.11.18",
41+
"chai": "^4.3.10",
3942
"mocha": "^10.2.0",
43+
"ts-node": "^10.9.1",
4044
"typescript": "^4.3.2"
4145
}
4246
}

src/HttpClient.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,21 @@ export default class ApiClient implements HttpClient {
132132

133133
private async parseResponse(response: any): Promise<string | Object> {
134134
const resJson = await response.text();
135+
const warrantToken = response.headers.get("Warrant-Token");
135136
if (resJson) {
136-
return JSON.parse(resJson);
137+
const parsedRes = JSON.parse(resJson);
138+
if (warrantToken != null) {
139+
parsedRes.warrantToken = warrantToken;
140+
}
141+
142+
return parsedRes;
137143
}
138144

139-
return resJson;
145+
if (warrantToken != null) {
146+
return {warrantToken: warrantToken};
147+
} else {
148+
return resJson;
149+
}
140150
}
141151

142152
private buildError(errorResponse: any): Error {

src/WarrantClient.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { API_URL_BASE } from "./constants";
22
import ApiClient from "./HttpClient";
33
import Authorization from "./modules/Authorization";
44
import Feature from "./modules/Feature";
5+
import Object from "./modules/ObjectModule";
56
import Permission from "./modules/Permission";
67
import PricingTier from "./modules/PricingTier";
78
import Role from "./modules/Role";
@@ -17,6 +18,7 @@ export default class WarrantClient {
1718

1819
public Authorization: typeof Authorization = Authorization;
1920
public Feature: typeof Feature = Feature;
21+
public Object: typeof Object = Object;
2022
public Permission: typeof Permission = Permission;
2123
public PricingTier: typeof PricingTier = PricingTier;
2224
public Role: typeof Role = Role;

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export const API_URL_BASE = "https://api.warrant.dev";
22
export const SELF_SERVICE_DASH_URL_BASE = "https://self-serve.warrant.dev";
3+
export const API_VERSION = "v2";

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
export * from "./types";
22
export { default as Feature } from "./modules/Feature";
3+
export { default as Object } from "./modules/ObjectModule";
34
export { default as Permission } from "./modules/Permission";
45
export { default as PricingTier } from "./modules/PricingTier";
56
export { default as Role } from "./modules/Role";
67
export { default as Tenant } from "./modules/Tenant";
78
export { default as User } from "./modules/User";
9+
export { default as Warrant } from "./modules/WarrantModule";
810
export { default as WarrantClient } from "./WarrantClient";

src/modules/Authorization.ts

Lines changed: 51 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,96 @@
1-
import Feature from "./Feature";
2-
import Permission from "./Permission";
3-
import Check, { AccessCheckRequest, CheckMany, CheckWarrant, FeatureCheck, PermissionCheck } from "../types/Check";
1+
import {
2+
Check,
3+
CheckParams,
4+
CheckManyParams,
5+
FeatureCheckParams,
6+
PermissionCheckParams,
7+
checkWarrantParamsToCheckWarrant,
8+
} from "../types/Check";
9+
import { ObjectType } from "../types/ObjectType";
410
import { WarrantRequestOptions } from "../types/WarrantRequestOptions";
5-
import Warrant, { isSubject, isWarrantObject } from "../types/Warrant";
611
import WarrantClient from "../WarrantClient";
12+
import { API_VERSION } from "../constants";
713

814
export default class Authorization {
9-
public static async check(check: Check, options: WarrantRequestOptions = {}): Promise<boolean> {
10-
const accessCheckRequest: AccessCheckRequest = {
11-
warrants: [{
12-
object: check.object,
13-
relation: check.relation,
14-
subject: check.subject,
15-
context: check.context
16-
}],
17-
debug: check.debug
15+
public static async check(checkParams: CheckParams, options: WarrantRequestOptions = {}): Promise<boolean> {
16+
const check: Check = {
17+
warrants: [checkWarrantParamsToCheckWarrant(checkParams)],
18+
debug: checkParams.debug
1819
}
1920
if (WarrantClient.config.authorizeEndpoint) {
20-
return this.edgeAuthorize(accessCheckRequest, options);
21+
return this.makeEdgeCheckRequest(check, options);
2122
}
2223

23-
return this.authorize(accessCheckRequest, options);
24+
return this.makeCheckRequest(check, options);
2425
}
2526

26-
public static async checkMany(check: CheckMany, options: WarrantRequestOptions = {}): Promise<boolean> {
27-
let warrants: CheckWarrant[] = check.warrants.map((warrant) => {
28-
return {
29-
object: warrant.object,
30-
relation: warrant.relation,
31-
subject: warrant.subject,
32-
context: warrant.context
33-
}
34-
})
35-
const accessCheckRequest: AccessCheckRequest = {
36-
op: check.op,
37-
warrants: warrants,
38-
debug: check.debug
39-
}
27+
public static async checkMany(checkParams: CheckManyParams, options: WarrantRequestOptions = {}): Promise<boolean> {
28+
const check: Check = {
29+
op: checkParams.op,
30+
warrants: checkParams.warrants.map((checkWarrantParams) => checkWarrantParamsToCheckWarrant(checkWarrantParams)),
31+
debug: checkParams.debug
32+
};
4033

4134
if (WarrantClient.config.authorizeEndpoint) {
42-
return this.edgeAuthorize(accessCheckRequest, options);
35+
return this.makeEdgeCheckRequest(check, options);
4336
}
4437

45-
return this.authorize(accessCheckRequest, options);
38+
return this.makeCheckRequest(check, options);
4639
}
4740

48-
public static async hasFeature(featureCheck: FeatureCheck, options: WarrantRequestOptions = {}): Promise<boolean> {
41+
public static async hasFeature(featureCheckParams: FeatureCheckParams, options: WarrantRequestOptions = {}): Promise<boolean> {
4942
return this.check({
50-
object: new Feature(featureCheck.featureId),
43+
object: {
44+
objectType: ObjectType.Feature,
45+
objectId: featureCheckParams.featureId,
46+
},
5147
relation: "member",
52-
subject: featureCheck.subject,
53-
context: featureCheck.context,
54-
debug: featureCheck.debug
48+
subject: featureCheckParams.subject,
49+
context: featureCheckParams.context,
50+
debug: featureCheckParams.debug
5551
}, options)
5652
}
5753

58-
public static async hasPermission(permissionCheck: PermissionCheck, options: WarrantRequestOptions = {}): Promise<boolean> {
54+
public static async hasPermission(permissionCheckParams: PermissionCheckParams, options: WarrantRequestOptions = {}): Promise<boolean> {
5955
return this.check({
60-
object: new Permission(permissionCheck.permissionId),
56+
object: {
57+
objectType: ObjectType.Permission,
58+
objectId: permissionCheckParams.permissionId,
59+
},
6160
relation: "member",
62-
subject: permissionCheck.subject,
63-
context: permissionCheck.context,
64-
debug: permissionCheck.debug
61+
subject: permissionCheckParams.subject,
62+
context: permissionCheckParams.context,
63+
debug: permissionCheckParams.debug
6564
}, options)
6665
}
6766

6867
// Private methods
69-
private static async authorize(accessCheckRequest: AccessCheckRequest, options: WarrantRequestOptions = {}): Promise<boolean> {
70-
try {
71-
72-
const response = await WarrantClient.httpClient.post({
73-
url: "/v2/authorize",
74-
data: {
75-
...accessCheckRequest,
76-
warrants: this.mapWarrantsForRequest(accessCheckRequest.warrants),
77-
},
78-
options,
79-
});
68+
private static async makeCheckRequest(check: Check, options: WarrantRequestOptions = {}): Promise<boolean> {
69+
const response = await WarrantClient.httpClient.post({
70+
url: `/${API_VERSION}/check`,
71+
data: check,
72+
options,
73+
});
8074

81-
return response.code === 200;
82-
} catch (e) {
83-
throw e;
84-
}
75+
return response.code === 200;
8576
}
8677

87-
private static async edgeAuthorize(accessCheckRequest: AccessCheckRequest, options: WarrantRequestOptions = {}): Promise<boolean> {
78+
private static async makeEdgeCheckRequest(check: Check, options: WarrantRequestOptions = {}): Promise<boolean> {
8879
try {
8980
const response = await WarrantClient.httpClient.post({
9081
baseUrl: WarrantClient.config.authorizeEndpoint,
91-
url: "/v2/authorize",
92-
data: {
93-
...accessCheckRequest,
94-
warrants: this.mapWarrantsForRequest(accessCheckRequest.warrants),
95-
},
82+
url: `/${API_VERSION}/check`,
83+
data: check,
9684
options,
9785
});
9886

9987
return response.code === 200;
10088
} catch (e) {
10189
if (e.code === "cache_not_ready") {
102-
return this.authorize(accessCheckRequest);
90+
return this.makeCheckRequest(check);
10391
}
10492

10593
throw e;
10694
}
10795
}
108-
109-
private static mapWarrantsForRequest(warrants: CheckWarrant[]): any[] {
110-
return warrants.map((warrant) => {
111-
return {
112-
objectType: isWarrantObject(warrant.object) ? warrant.object.getObjectType() : warrant.object.objectType,
113-
objectId: isWarrantObject(warrant.object) ? warrant.object.getObjectId() : warrant.object.objectId,
114-
relation: warrant.relation,
115-
subject: isSubject(warrant.subject) ? warrant.subject : { objectType: warrant.subject.getObjectType(), objectId: warrant.subject.getObjectId() },
116-
context: warrant.context
117-
}
118-
})
119-
}
12096
}

0 commit comments

Comments
 (0)