github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/gubernator/github/models.py (about) 1 # Copyright 2016 The Kubernetes Authors. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 import datetime 16 import json 17 18 import google.appengine.ext.ndb as ndb 19 20 21 class GithubResource(ndb.Model): 22 # A key holder used to define an entitygroup for 23 # each Issue/PR, for easy ancestor queries. 24 @staticmethod 25 def make_key(repo, number): 26 return ndb.Key(GithubResource, '%s %s' % (repo, number)) 27 28 29 def shrink(body): 30 ''' 31 Recursively remove Github API urls from an object, to make it 32 more human-readable. 33 ''' 34 toremove = [] 35 for key, value in body.iteritems(): 36 if isinstance(value, basestring): 37 if key.endswith('url'): 38 if (value.startswith('https://api.github.com/') or 39 value.startswith('https://avatars.githubusercontent.com')): 40 toremove.append(key) 41 elif isinstance(value, dict): 42 shrink(value) 43 elif isinstance(value, list): 44 for el in value: 45 if isinstance(el, dict): 46 shrink(el) 47 for key in toremove: 48 body.pop(key) 49 return body 50 51 52 class GithubWebhookRaw(ndb.Model): 53 repo = ndb.StringProperty() 54 number = ndb.IntegerProperty(indexed=False) 55 event = ndb.StringProperty() 56 timestamp = ndb.DateTimeProperty(auto_now_add=True) 57 body = ndb.TextProperty(compressed=True) 58 59 def to_tuple(self): 60 return (self.event, shrink(json.loads(self.body)), float(self.timestamp.strftime('%s.%f'))) 61 62 63 def from_iso8601(t): 64 return t and datetime.datetime.strptime(t, '%Y-%m-%dT%H:%M:%SZ') 65 66 67 def make_kwargs(body, fields): 68 kwargs = {} 69 for field in fields: 70 if field.endswith('_at'): 71 kwargs[field] = from_iso8601(body[field]) 72 else: 73 kwargs[field] = body[field] 74 return kwargs 75 76 77 class GHStatus(ndb.Model): 78 # Key: {repo}\t{sha}\t{context} 79 state = ndb.StringProperty(indexed=False) 80 target_url = ndb.StringProperty(indexed=False) 81 description = ndb.TextProperty() 82 83 created_at = ndb.DateTimeProperty(indexed=False) 84 updated_at = ndb.DateTimeProperty(indexed=False) 85 86 87 @staticmethod 88 def make_key(repo, sha, context): 89 return ndb.Key(GHStatus, '%s\t%s\t%s' % (repo, sha, context)) 90 91 @staticmethod 92 def make(repo, sha, context, **kwargs): 93 return GHStatus(key=GHStatus.make_key(repo, sha, context), **kwargs) 94 95 @staticmethod 96 def query_for_sha(repo, sha): 97 before = GHStatus.make_key(repo, sha, '') 98 after = GHStatus.make_key(repo, sha, '\x7f') 99 return GHStatus.query(GHStatus.key > before, GHStatus.key < after) 100 101 @staticmethod 102 def from_json(body): 103 kwargs = make_kwargs(body, 104 'sha context state target_url description ' 105 'created_at updated_at'.split()) 106 kwargs['repo'] = body['name'] 107 return GHStatus.make(**kwargs) 108 109 @property 110 def repo(self): 111 return self.key.id().split('\t', 1)[0] 112 113 @property 114 def sha(self): 115 return self.key.id().split('\t', 2)[1] 116 117 @property 118 def context(self): 119 return self.key.id().split('\t', 2)[2] 120 121 122 class GHIssueDigest(ndb.Model): 123 # Key: {repo} {number} 124 is_pr = ndb.BooleanProperty() 125 is_open = ndb.BooleanProperty() 126 involved = ndb.StringProperty(repeated=True) 127 xref = ndb.StringProperty(repeated=True) 128 payload = ndb.JsonProperty() 129 updated_at = ndb.DateTimeProperty() 130 head = ndb.StringProperty() 131 132 @staticmethod 133 def make_key(repo, number): 134 return ndb.Key(GHIssueDigest, '%s %s' % (repo, number)) 135 136 @staticmethod 137 def make(repo, number, is_pr, is_open, involved, payload, updated_at): 138 return GHIssueDigest(key=GHIssueDigest.make_key(repo, number), 139 is_pr=is_pr, is_open=is_open, involved=involved, payload=payload, 140 updated_at=updated_at, head=payload.get('head'), 141 xref=payload.get('xrefs', [])) 142 143 @staticmethod 144 def get(repo, number): 145 return GHIssueDigest.make_key(repo, number).get() 146 147 @property 148 def repo(self): 149 return self.key.id().split()[0] 150 151 @property 152 def number(self): 153 return int(self.key.id().split()[1]) 154 155 @staticmethod 156 def find_head(repo, head): 157 return GHIssueDigest.query(GHIssueDigest.key > GHIssueDigest.make_key(repo, ''), 158 GHIssueDigest.key < GHIssueDigest.make_key(repo, '~'), 159 GHIssueDigest.head == head) 160 161 @staticmethod 162 def find_xrefs(xref): 163 return GHIssueDigest.query(GHIssueDigest.xref == xref) 164 165 166 class GHUserState(ndb.Model): 167 # Key: {github username} 168 acks = ndb.JsonProperty() # dict of issue keys => ack time (seconds since epoch) 169 170 @staticmethod 171 def make_key(user): 172 return ndb.Key(GHUserState, user) 173 174 @staticmethod 175 def make(user, acks=None): 176 return GHUserState(key=GHUserState.make_key(user), acks=acks or {}) 177 178 179 @ndb.transactional 180 def save_if_newer(obj): 181 assert obj.updated_at is not None 182 old = obj.key.get() 183 if old is None: 184 obj.put() 185 return True 186 else: 187 if old.updated_at is None or obj.updated_at >= old.updated_at: 188 obj.put() 189 return True 190 return False