Skip to content

Commit

Permalink
feat: support file-level resource annotations (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-fenster committed Dec 10, 2019
1 parent 56569ee commit e71cb5c
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 108 deletions.
130 changes: 65 additions & 65 deletions typescript/src/schema/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,75 +112,75 @@ export class API {
}
}

function processOneResource(
option: ResourceDescriptor | undefined,
fileAndMessageNames: string,
resourceMap: ResourceMap
): void {
if (!option) {
return;
}
if (!option.type) {
console.warn(
`Warning: in ${fileAndMessageNames} refers to a resource which does not have a type: ${option}`
);
return;
}

const arr = option.type.match(/\/([^.]+)$/);
if (!arr?.[1]) {
console.warn(
`Warning: in ${fileAndMessageNames} refers to a resource which does not have a proper name: ${option}`
);
return;
}
option.name = arr[1];

const pattern = option.pattern;
if (!pattern?.[0]) {
console.warn(
`Warning: in ${fileAndMessageNames} refers to a resource which does not have a proper pattern: ${option}`
);
return;
}
const params = pattern[0].match(/{[a-zA-Z]+}/g) || [];
for (let i = 0; i < params.length; i++) {
params[i] = params[i].replace('{', '').replace('}', '');
}
option.params = params;

resourceMap[option.type!] = option;
}

function getResourceMap(
fileDescriptors: plugin.google.protobuf.IFileDescriptorProto[]
): ResourceMap {
const resourceMap: ResourceMap = {};
for (const fd of fileDescriptors) {
if (fd && fd.messageType) {
const messages = fd.messageType
.filter(message => message.name)
.reduce((map, message) => {
map['.' + fd.package! + '.' + message.name!] = message;
return map;
}, {} as MessagesMap);
for (const property of Object.keys(messages)) {
const m = messages[property];
if (m?.options) {
const option = m.options;
if (option?.['.google.api.resource']) {
const opt = option['.google.api.resource'];
const oneResource = option[
'.google.api.resource'
] as ResourceDescriptor;
if (opt.type) {
const arr = opt.type.match(/\/([^.]+)$/);
if (arr?.[1]) {
oneResource.name = arr[1];
}
} else {
console.warn(
'In file ' +
fd.name +
' message ' +
property +
' refers to a resource which does not have a type: ' +
opt
);
continue;
}
const pattern = opt.pattern;
if (pattern?.[0]) {
const params = pattern[0].match(/{[a-zA-Z]+}/g) || [];
for (let i = 0; i < params.length; i++) {
params[i] = params[i].replace('{', '').replace('}', '');
}
oneResource.params = params;
}
if (oneResource.name && oneResource.params) {
resourceMap[opt.type!] = oneResource;
} else if (oneResource.name) {
console.warn(
'Warning: in file ' +
fd.name +
' message ' +
property +
' refers to a resource which does not have a proper pattern : ' +
opt
);
} else {
console.warn(
'Warning: in file ' +
fd.name +
' message ' +
property +
' refers to a resource which does not have a proper name : ' +
opt
);
}
}
}
}
for (const fd of fileDescriptors.filter(fd => fd)) {
// process file-level options
for (const resource of fd.options?.['.google.api.resourceDefinition'] ??
[]) {
processOneResource(
resource as ResourceDescriptor,
`file ${fd.name} resource_definition option`,
resourceMap
);
}

const messages = (fd.messageType ?? [])
.filter(message => message.name)
.reduce((map, message) => {
map['.' + fd.package! + '.' + message.name!] = message;
return map;
}, {} as MessagesMap);

for (const property of Object.keys(messages)) {
const m = messages[property];
processOneResource(
m?.options?.['.google.api.resource'] as ResourceDescriptor | undefined,
`file ${fd.name} message ${property}`,
resourceMap
);
}
}
return resourceMap;
Expand Down
9 changes: 2 additions & 7 deletions typescript/src/schema/proto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import * as plugin from '../../../pbjs-genfiles/plugin';
import { CommentsMap, Comment } from './comments';
import * as objectHash from 'object-hash';
import { milliseconds } from '../util';
import { FileSystemLoader } from 'nunjucks';

const defaultNonIdempotentRetryCodesName = 'non_idempotent';
const defaultNonIdempotentCodes: plugin.google.rpc.Code[] = [];
Expand Down Expand Up @@ -540,13 +539,9 @@ function augmentService(
const resourceReference = option['.google.api.resourceReference'];
const type = resourceReference.type;
if (!type || !resourceMap[type.toString()]) {
const resourceJson = JSON.stringify(resourceReference);
console.warn(
'Warning: in service proto ' +
service.name +
' message ' +
property +
' refers to an unknown resource: ' +
JSON.stringify(resourceReference)
`Warning: in service proto ${service.name} message ${property} refers to an unknown resource: ${resourceJson}`
);
continue;
}
Expand Down
56 changes: 20 additions & 36 deletions typescript/test/protos/google/cloud/common_resources.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,47 +22,31 @@ package google.cloud;
import "google/api/resource.proto";


message Project {
option (google.api.resource) = {
type: "cloudresourcemanager.googleapis.com/Project"
pattern: "projects/{project}"
};
option (google.api.resource_definition) = {
type: "cloudresourcemanager.googleapis.com/Project"
pattern: "projects/{project}"
};

string name = 1;
}

message Organization {
option (google.api.resource) = {
type: "cloudresourcemanager.googleapis.com/Organization"
pattern: "organizations/{organization}"
};
option (google.api.resource_definition) = {
type: "cloudresourcemanager.googleapis.com/Organization"
pattern: "organizations/{organization}"
};

string name = 1;
}

message Folder {
option (google.api.resource) = {
type: "cloudresourcemanager.googleapis.com/Folder"
pattern: "folders/{folder}"
};
option (google.api.resource_definition) = {
type: "cloudresourcemanager.googleapis.com/Folder"
pattern: "folders/{folder}"
};

string name = 1;
}

message BillingAccount {
option (google.api.resource) = {
type: "cloudbilling.googleapis.com/BillingAccount"
pattern: "billingAccounts/{billing_account}"
};
option (google.api.resource_definition) = {
type: "cloudbilling.googleapis.com/BillingAccount"
pattern: "billingAccounts/{billing_account}"
};

string name = 1;
}
option (google.api.resource_definition) = {
type: "locations.googleapis.com/Location"
pattern: "projects/{project}/locations/{location}"
};

message Location {
option (google.api.resource) = {
type: "locations.googleapis.com/Location"
pattern: "projects/{project}/locations/{location}"
};

string name = 1;
}

0 comments on commit e71cb5c

Please sign in to comment.