The "Room Specification" (or "Room Version Specification") is the specification that defines which room versions do what and are intended to be documents which speak the truth about how rooms operate under the hood. The approach taken here is a bit different than other specifications. For starters, the specification is versioned in this project instead of relying on the matrix.org repository to track compiled HTML. This is done for a couple reasons, the first being we're still developing the v1 specification while concurrently making a v2 spec and the second being trying to reduce the reliance on matrix.org's repository for specifications. Because the room spec is built into versions, some changes needed to be made. The `targets.yaml` now has a special syntax for indicating what version something is at, and the changelog generator can handle rendering different versions of the same changelog (as parsed from the RST). Some additional work has been put in to the changelog parsing to allow us to reference the v1 room spec as "v1" without having to sacrifice clarity in the changelog headings. Finally, this moves the state resolution algorithms into the versioned spec as a result of MSC1759 (https://github.com/matrix-org/matrix-doc/pull/1759). Note: this does not introduce the concept of versioned schemas (tabs) that I was previously working with. There's currently no use for them, so they are shelved elsewhere.
225 lines
8.4 KiB
Python
225 lines
8.4 KiB
Python
# Copyright 2016 OpenMarket Ltd
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Contains all the sections for the spec."""
|
|
from batesian import AccessKeyStore
|
|
from batesian.sections import Sections
|
|
import inspect
|
|
import json
|
|
import os
|
|
import logging
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class MatrixSections(Sections):
|
|
|
|
# pass through git ver so it'll be dropped in the input file
|
|
def render_git_version(self):
|
|
return self.units.get("git_version")["string"]
|
|
|
|
def render_git_rev(self):
|
|
return self.units.get("git_version")["revision"]
|
|
|
|
def render_changelogs(self):
|
|
rendered = {}
|
|
changelogs = self.units.get("changelogs")
|
|
for spec, versioned in changelogs.items():
|
|
spec_var = "%s_changelog" % spec
|
|
logger.info("Rendering changelog for spec: %s" % spec)
|
|
for version, changelog in versioned.items():
|
|
version_var = "%s_%s" % (spec_var, version)
|
|
logger.info("Rendering changelog for %s" % version_var)
|
|
rendered[version_var] = changelog
|
|
if version == "unstable":
|
|
rendered[spec_var] = changelog
|
|
return rendered
|
|
|
|
def _render_events(self, filterFn, sortFn):
|
|
template = self.env.get_template("events.tmpl")
|
|
examples = self.units.get("event_examples")
|
|
schemas = self.units.get("event_schemas")
|
|
subtitle_title_char = self.units.get("spec_targets")[
|
|
"relative_title_styles"
|
|
]["subtitle"]
|
|
sections = []
|
|
for event_name in sortFn(schemas):
|
|
if not filterFn(event_name):
|
|
continue
|
|
sections.append(template.render(
|
|
examples=examples[event_name],
|
|
event=schemas[event_name],
|
|
title_kind=subtitle_title_char
|
|
))
|
|
return "\n\n".join(sections)
|
|
|
|
def _render_http_api_group(self, group, sortPathList=None):
|
|
template = self.env.get_template("http-api.tmpl")
|
|
http_api = self.units.get("swagger_apis")[group]["__meta"]
|
|
subtitle_title_char = self.units.get("spec_targets")[
|
|
"relative_title_styles"
|
|
]["subtitle"]
|
|
sections = []
|
|
endpoints = []
|
|
if sortPathList:
|
|
# list of substrings to sort by
|
|
sorted_endpoints = []
|
|
for path_substr in sortPathList:
|
|
for e in http_api["endpoints"]:
|
|
if path_substr in e["path"]:
|
|
sorted_endpoints.append(e) # could have multiple
|
|
# dump rest
|
|
rest = [
|
|
e for e in http_api["endpoints"] if e not in sorted_endpoints
|
|
]
|
|
endpoints = sorted_endpoints + rest
|
|
else:
|
|
# sort alphabetically based on path
|
|
endpoints = http_api["endpoints"]
|
|
|
|
for endpoint in endpoints:
|
|
sections.append(template.render(
|
|
endpoint=endpoint,
|
|
title_kind=subtitle_title_char
|
|
))
|
|
return "\n\n".join(sections)
|
|
|
|
|
|
# Special function: Returning a dict will specify multiple sections where
|
|
# the key is the section name and the value is the value of the section
|
|
def render_group_http_apis(self):
|
|
# map all swagger_apis to the form $GROUP_http_api
|
|
swagger_groups = self.units.get("swagger_apis").keys()
|
|
renders = {}
|
|
for group in swagger_groups:
|
|
sortFnOrPathList = None
|
|
if group == "presence_cs":
|
|
sortFnOrPathList = ["status"]
|
|
elif group == "profile_cs":
|
|
sortFnOrPathList=["displayname", "avatar_url"]
|
|
renders[group + "_http_api"] = self._render_http_api_group(
|
|
group, sortFnOrPathList
|
|
)
|
|
return renders
|
|
|
|
# Special function: Returning a dict will specify multiple sections where
|
|
# the key is the section name and the value is the value of the section
|
|
def render_group_events(self):
|
|
# map all event schemata to the form $EVENTTYPE_event with s/./_/g
|
|
# e.g. m_room_topic_event
|
|
schemas = self.units.get("event_schemas")
|
|
renders = {}
|
|
for event_type in schemas:
|
|
renders[event_type.replace(".", "_") + "_event"] = self._render_events(
|
|
lambda x: x == event_type, sorted
|
|
)
|
|
return renders
|
|
|
|
def render_room_events(self):
|
|
def filterFn(eventType):
|
|
return (
|
|
eventType.startswith("m.room") and
|
|
not eventType.startswith("m.room.message#m.")
|
|
)
|
|
return self._render_events(filterFn, sorted)
|
|
|
|
def render_msgtype_events(self):
|
|
template = self.env.get_template("msgtypes.tmpl")
|
|
examples = self.units.get("event_examples")
|
|
schemas = self.units.get("event_schemas")
|
|
subtitle_title_char = self.units.get("spec_targets")[
|
|
"relative_title_styles"
|
|
]["subtitle"]
|
|
sections = []
|
|
msgtype_order = [
|
|
"m.room.message#m.text", "m.room.message#m.emote",
|
|
"m.room.message#m.notice", "m.room.message#m.image",
|
|
"m.room.message#m.file"
|
|
]
|
|
other_msgtypes = [
|
|
k for k in schemas.keys() if k.startswith("m.room.message#") and
|
|
k not in msgtype_order
|
|
]
|
|
for event_name in (msgtype_order + other_msgtypes):
|
|
if not event_name.startswith("m.room.message#m."):
|
|
continue
|
|
sections.append(template.render(
|
|
example=examples[event_name][0],
|
|
event=schemas[event_name],
|
|
title_kind=subtitle_title_char
|
|
))
|
|
return "\n\n".join(sections)
|
|
|
|
def render_voip_events(self):
|
|
def filterFn(eventType):
|
|
return eventType.startswith("m.call")
|
|
def sortFn(eventTypes):
|
|
ordering = [
|
|
"m.call.invite", "m.call.candidates", "m.call.answer",
|
|
"m.call.hangup"
|
|
]
|
|
rest = [
|
|
k for k in eventTypes if k not in ordering
|
|
]
|
|
return ordering + rest
|
|
return self._render_events(filterFn, sortFn)
|
|
|
|
def render_presence_events(self):
|
|
def filterFn(eventType):
|
|
return eventType.startswith("m.presence")
|
|
return self._render_events(filterFn, sorted)
|
|
|
|
def _render_ce_type(self, type):
|
|
template = self.env.get_template("common-event-fields.tmpl")
|
|
ce_types = self.units.get("common_event_fields")
|
|
subtitle_title_char = self.units.get("spec_targets")[
|
|
"relative_title_styles"
|
|
]["subtitle"]
|
|
return template.render(
|
|
common_event=ce_types[type], title_kind=subtitle_title_char
|
|
)
|
|
|
|
def render_common_event_fields(self):
|
|
return self._render_ce_type("event")
|
|
|
|
def render_common_room_event_fields(self):
|
|
return self._render_ce_type("room_event")
|
|
|
|
def render_common_state_event_fields(self):
|
|
return self._render_ce_type("state_event")
|
|
|
|
def render_apis(self):
|
|
template = self.env.get_template("apis.tmpl")
|
|
apis = self.units.get("apis")
|
|
return template.render(apis=apis)
|
|
|
|
def render_unstable_warnings(self):
|
|
rendered = {}
|
|
blocks = self.units.get("unstable_warnings")
|
|
for var, text in blocks.items():
|
|
rendered["unstable_warning_block_" + var] = text
|
|
return rendered
|
|
|
|
def render_swagger_definition(self):
|
|
rendered = {}
|
|
template = self.env.get_template("schema-definition.tmpl")
|
|
subtitle_title_char = self.units.get("spec_targets")[
|
|
"relative_title_styles"
|
|
]["subtitle"]
|
|
definitions = self.units.get("swagger_definitions")
|
|
for group, swagger_def in definitions.items():
|
|
rendered["definition_" + group] = template.render(
|
|
definition=swagger_def['definition'],
|
|
examples=swagger_def['examples'],
|
|
title_kind=subtitle_title_char)
|
|
return rendered
|